設計模式-用代理模式(Proxy Pattern)來拯救你的代碼:打造可靠的程式設計

来源:https://www.cnblogs.com/GarveyCalvin/archive/2023/03/25/proxy-pattern.html
-Advertisement-
Play Games

代理模式(Proxy Pattern)是一種結構型設計模式,結構型模式描述如何將類或對象按某種佈局組成更大的結構。它允許你提供一個代理對象來控制對另一個對象的訪問。代理對象擁有與實際對象相同的介面,因此它可以被用來代替實際對象。 ...


前言

設計模式是一種高級編程技巧,也是一種通用的解決方案。它能在不同的應用場景中使用,它可以提高代碼的可讀性、可復用性和可維護性。設計模式的學習能提高我們的編程能力以及代碼質量,同時也能提高我們的開發效率,減少代碼的維護成本。

掌握設計模式對於開發軟體而言是非常重要的,熟練地應用設計模式能讓我們更加自信地去編寫程式,另一方面,這也對我們的面試至關重要,這可是妥妥的加分項呢。所以為了我們的代碼質量和未來發展而言,我們都要對設計模式有一定的瞭解和應用。

設計模式有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
公眾號:凡人程式猿
本文版權歸作者所有,歡迎轉載,但必須保留此段聲明,並給出原文鏈接,謝謝合作!

活著,就是為了改變世界!
您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 一、工具介紹 YCSB 於 2010 年開源,YCSB是雅虎開源的NoSQL測試工具,用java開發實現,通常用來對noSQL資料庫進行性能,註意此工具僅支持varchar和text類型,且列的長度可以增加,預設是10列,可以根據自己的需要增加列長。運行一個壓力測試需要 6 步: 配置需要測試的數據 ...
  • 一、JDK(Java Development Kit)安裝 版本: 資源:下載官網的資源需要登錄帳號,可以在網上自己去找資源。jdk8 下載地址 1、打開jdk安裝軟體,進入Java SE 安裝界面。 2、點擊下一步。 3、點擊下一步,進入安裝界面。安裝完成後進入Java安裝界面。 4、點擊下一步, ...
  • Vuex概述 組件之間共用數據的方式(小範圍) 全局事件匯流排 Vuex是什麼? 專門在Vue中實現集中式狀態(數據)管理的一個Vue插件,可以方便的實現組件之間的數據共用。 使用Vuex統一管理狀態的好處 能夠在vuex中集中管理共用的數據,易於開發和後期維護 能夠高效地實現組件之間的數據共用,提高 ...
  • 前言 前面我們簡單的瞭解了 vue 初始化時的一些大概的流程,這裡我們詳細的瞭解下具體的內容; 內容 這一塊主要圍繞init.ts中的initState進行剖析,初始化生命周期之後緊接著。 initState initState的方法位於scr/core/instance/state.ts中; co ...
  • 定義 代理是一個中間者的角色,如生活中的中介,出於種種考慮/限制,一個對象不能直接訪問另一個對象,需要一個第三者(中間代理)牽線搭橋從而間接達到訪問目的,這樣的就是代理模式。 es6 中的代理 es6 的 proxy 就是上面說的代理模式的實現,es6 幫我們在語法層面提供了這個新的api,讓我們可 ...
  • 使用工具: IDEA2022 Tomcat9.0.4 1.下載Tomcat: 官網:https://tomcat.apache.org/ 找到需要的版本下載即可,下載完成解壓即可用: Tomcat目錄介紹: 1.1.Tomcat啟動、關閉。卸載: 啟動:雙擊bin\startup.bat 關閉:直接 ...
  • 委托模式(Delegation pattern):將一個對象的某個方法委托給另一個對象來執行,它可以幫助我們將對象之間的關係更加靈活地組織起來,從而提高代碼的可維護性和復用性。 在委托模式中,一個對象(稱為委托對象)將一些特定的任務委托給另一個對象(稱為代理對象)來執行。代理對象通常具有和委托對象相 ...
  • 1.聲明與變數 let聲明的變數可以多次賦值 let 變數名 = 值; const修飾叫常量,只能賦值一次,但是引用的值可以改變 var聲明的變數可以多次賦值 結論:能用let不用var ,因為作用域的問題 2.基本類型和對象類型 undefined 和 null undefined 指 未定義的對 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...