前言
設計模式是一種高級編程技巧,也是一種通用的解決方案,它能在不同的應用場景中使用,它可以提高代碼的可讀性、可復用性和可維護性,設計模式的學習能提高我們的編程能力以及代碼質量,同時也能提高我們的開發效率,減少代碼的維護成本,
掌握設計模式對于開發軟體而言是非常重要的,熟練地應用設計模式能讓我們更加自信地去撰寫程式,另一方面,這也對我們的面試至關重要,這可是妥妥的加分項呢,所以為了我們的代碼質量和未來發展而言,我們都要對設計模式有一定的了解和應用,
設計模式有23種,在一篇文章中全部講完肯定是不可能的,這篇文章會先介紹 代理模式(Proxy Pattern),本文的代碼示例用的是 Swift 語言撰寫,
代理模式(Proxy Pattern)
代理模式(Proxy Pattern)是一種結構型設計模式,結構型模式描述如何將類或物件按某種布局組成更大的結構,它允許你提供一個代理物件來控制對另一個物件的訪問,代理物件擁有與實際物件相同的介面,因此它可以被用來代替實際物件,
代理物件可以在呼叫實際物件之前或之后執行一些額外的操作,例如記錄日志、快取資料、控制訪問權限等,這種模式通常被用于遠程代理、虛擬代理(Virtual Proxy)、保護代理和延遲加載等應用場景,
代理模式:為其他物件提供一種代理以控制對這個物件的訪問,
最 初 的 定 義 出 現 于 《設 計 模 式 》 ( A d d i s o n - W e s l e y , 1 9 9 4 ) ,
代碼示例
代理模式(Proxy Pattern)通用類圖

Subject主題介面,定義了真實主題和代理主題的公共介面,客戶端可以通過主題介面來訪問真實主題或者代理主題,RealSubject真實主題,實作了主題介面,定義了真正的業務邏輯,Proxy代理主題,也實作了主題介面,同時還持有了一個真實主題的參考,客戶端通過代理主題來訪問真實主題,代理主題可以對訪問進行控制,
以下是通用代碼:
protocol Subject {
func request()
}
class RealSubject: Subject {
func request() {
print("RealSubject handling request")
}
}
class Proxy: Subject {
private let realSubject = RealSubject()
func request() {
print("Proxy handling request")
realSubject.request()
}
}
客戶端呼叫代碼:
let subject = Proxy()
subject.request()
偽代碼(老默,我想吃魚了)
突然想到了一個很有趣的例子,比如說我現在想吃魚,那吃魚得先去菜市場把魚買回來吧,一般來說都是自己去菜市場里買魚,但是我現在只想吃魚而不想親自去買魚,那我就和代理人說一聲,說我想吃魚了,代理人就會代替我去菜市場把魚帶回來給我,亦或者是代理人找的別人去買也是可以的,偽代碼如下所示:
protocol 主題介面 {
func 去買魚()
}
class 老默: 主題介面 {
func 去買魚() {
print("把魚買回來了")
}
}
class 代理人: 主題介面 {
private let 我是老默 = 老默()
func 去買魚() {
我是老默.去買魚()
}
}
客戶端呼叫的偽代碼如下所示:
let 我是一個代理 = 代理類()
我是一個代理.去買魚()
每次想吃魚我們就找到代理人,由它來安排,至于誰去我也不在乎,
虛擬代理(Virtual Proxy)
我們拿虛擬代理(Virtual Proxy)來作為例子講解說明,它是代理模式(Proxy Pattern)的一種,它在代理模式的應用中比較常見,
虛擬代理(Virtual Proxy)控制訪問創建開銷大的資源,其通過在代理物件和實際物件之間添加一層代理層,來實作對實際物件的延遲加載或快取,

ImageLoader定義一個協議,規定了代理物件和實際物件需要實作的方法,RealImageLoader是遵循ImageLoader的實際物件,是實際做事的圖片加載器,ImageLoaderProxy是遵循ImageLoader的代理物件,它持有RealImageLoader的參考,并且還實作了一個簡單的圖片快取機制,以避免重復下載相同的圖片,
我們首先撰寫 ImageLoader 協議,定義相同的介面方法:
protocol ImageLoader {
func loadImage(url: URL, completion: @escaping (UIImage?) -> Void)
}
接下來,撰寫圖片加載器 RealImageLoader 類,并遵循 ImageLoader 協議:
class RealImageLoader: ImageLoader {
func loadImage(url: URL, completion: @escaping (UIImage?) -> Void) {
URLSession.shared.dataTask(with: url) { data, response, error in
guard let data = data, error == nil else {
completion(nil)
return
}
let image = UIImage(data: data)
completion(image)
}.resume()
}
}
我們使用 URLSession 來執行一個異步的網路請求,將圖片資料下載下來,并將其轉換為 UIImage 物件并傳遞給回呼閉包,
接下來,撰寫 ImageLoaderProxy 代理類:
class ImageLoaderProxy: ImageLoader {
private let realImageLoader = RealImageLoader()
private var cachedImages: [URL: UIImage] = [:]
func loadImage(url: URL, completion: @escaping (UIImage?) -> Void) {
if let cachedImage = cachedImages[url] {
completion(cachedImage)
} else {
completion(UIImage(named: "image_loading_bg"))
realImageLoader.loadImage(url: url) { [weak self] image in
guard let self = self else { return }
if let image = image {
self.cachedImages[url] = image
}
completion(image)
}
}
}
}
當客戶端呼叫 loadImage(url:completion:) 方法時,代理物件首先會判斷是否已經快取了對應的圖片,如果已經快取,則直接回傳快取的圖片,否則先傳遞一個默認的加載圖片用于顯示過渡,再使用真正的圖片加載器 RealImageLoader 加載圖片,并在加載完成后快取圖片,
最后撰寫客戶端的呼叫代碼:
let imageLoader: ImageLoader = ImageLoaderProxy()
if let url = URL(string: "https://img2.baidu.com/it/u=2082637540,462915030&fm=253&fmt=auto&app=138&f=JPEG?w=500&h=888") {
imageLoader.loadImage(url: url) { [weak self] image in
DispatchQueue.main.async {
self?.imageView.image = image
}
}
}
這里需要注意的是,imageLoader在網路請求的程序中不能被釋放,否則閉包里的 self 會為空,你可以放在 controller 的屬性當中,
DispatchQueue.main.async 方法使執行緒回到主執行緒執行圖片的加載,
效果呈現
建議在開發者工具當中設定你的網路,將其設定為低速率的,這樣方便我們查看效果,


總結
代理模式(Proxy Pattern)允許你提供一個代理物件來控制對另一個物件的訪問,隱藏具體的實作細節,一定程式上降低了系統的耦合度,可以起到保護目標物件的傷,可以對目標物件的功能增加,如本文介紹虛擬代理使用的圖片加載例子,在其中加入了圖片快取就是這個形式,
當然它也是有缺點的,使用代理模式(Proxy Pattern)可能會使類的數量增加,也可能會增加代碼的復雜度,因為它涉及到多個物件之間的協作,代理模式可能會導致有性能損失,因為客戶端需要通過代理物件來訪問真實物件,從而增加了額外的開銷,
結語
本文章的代碼已經整理到 GitHub 上,請點擊鏈接獲取,
記得以前看過的一個新聞,有一句結尾的話印象挺深刻的,分享出來給大家:“時間過得真系快,又系時候講拜拜”,話題轉回來,我會定期發布一些技術文章,如果這篇文章對你有幫助,請你關注我,
關于作者
博文作者:GarveyCalvin
公眾號:凡人程式猿
本文著作權歸作者所有,歡迎轉載,但必須保留此段宣告,并給出原文鏈接,謝謝合作!
轉載請註明出處,本文鏈接:https://www.uj5u.com/ruanti/548188.html
標籤:設計模式
上一篇:談談架構設計
