掌握C# 中的代理設計模式(Proxy Design Pattern)
什么是代理設計模式?
根據 Gang of four 的定義,代理設計模式為另一個對象提供了一個代理(代表其他行動人)或占位符來控制對它的訪問。 代理是指“代替”或“代表”。
在最簡單的形式中,我們可以將代理定義為一個類,作為其他東西的接口。 代理可以連接到任何東西,例如網絡連接、內存中的大型對象、文件或其他一些昂貴或無法復制的資源。
我們也可以說代理(Proxy)是客戶端(Client)調用的對象,用于訪問幕后的真實對象。 這意味著,在代理設計模式中,一個類代表另一個類的功能。
通過示例了解 C# 中的代理設計模式:
請查看下圖以更好地理解 C# 中的代理設計模式。 正如您在下圖中看到的,當客戶端想要使用真實對象的某些方法時,他/她需要通過代理對象。 這意味著客戶端將調用代理對象的方法,而代理將負責調用真實對象的方法。
代理的類型:
有三種類型的代理。 它們如下。
- 虛擬代理:虛擬代理是“昂貴創建”對象的占位符。 真正的對象僅在客戶端首次請求或訪問該對象時創建。
- 遠程代理:遠程代理為駐留在不同地址空間中的對象提供本地表示。
- 保護代理:保護代理控制對敏感對象的訪問。 代理對象在轉發請求之前檢查調用者是否具有所需的訪問權限。
代理設計模式真實示例:
請看下圖。 在右側,您可以看到銀行,在左側可以看到一個叫 Anurag 的人。 Anurag 在銀行有一個賬戶。 在早些時候,比如說 1960 年,Anurag 想從他的賬戶中取款。 那么他要做的就是,他必須帶著他的存折去銀行。 然后他必須填寫表格并需要排隊。 輪到他時,他必須將表格和銀行存折交給銀行員工,然后銀行員工驗證表格和他的存折,如果一切正常,則銀行員工將所需的錢交給 Anurag。
假設 Anurag 現在想取錢。 因此,他現在能做的不是去銀行,而是拿著銀行卡到最近的 ATM。 然后他插入他的銀行卡并輸入密碼和提款金額。 然后 ATM 將與銀行通信并驗證密碼和金額,如果一切正常,ATM 將把錢交給 Anurag。 Anurag 無需去銀行,直接從 ATM 取款。 因此,這里的銀行是真實對象,ATM 是代理。 我認為這是代理設計模式的最佳真實示例。
為什么我們需要 C# 中的代理設計模式?
讓我們以代理服務器為例來了解代理設計模式的必要性。
位于客戶端應用程序(例如 Web 瀏覽器)和真實服務器之間的服務器稱為代理服務器。 該代理服務器攔截所有傳入的真實服務器請求,以查看它是否可以自行完成請求。 如果不是,那么它會將請求轉發到真實服務器。
代理服務器有兩個主要目標。 它們如下:
提高性能:
代理服務器可以極大地提高應用程序的性能。 這是因為它將請求的結果保存了一段時間。 例如,假設我們有兩個用戶 X 和 Y,他們想通過代理服務器訪問特定資源。 首先,用戶 X 請求一個特定的資源(比方說一個員工列表)并將該資源緩存一段時間。 稍后,用戶 Y 也請求相同的資源,代理服務器不再將該請求轉發給實際服務器(這是一項耗時操作),只需從緩存中返回數據即可。 由于客戶端和代理服務器在同一個網絡中,因此操作速度會快得多。
過濾請求:
代理服務器也可用于過濾傳入的請求。 例如,一家公司可能會使用代理服務器來阻止其員工訪問一組特定的網站,如某寶、拼某多等。
C#中代理設計模式的實現(保護代理):
業務要求:
請看下圖。 正如您在下圖中看到的,在右側我們有一臺共享文件夾的共享計算機。 在左側,我們有在軟件農場工作的員工。 共享計算機包含一個包含機密信息的共享文件夾,只有具有經理和首席執行官角色的員工才能訪問此共享文件夾并執行讀寫操作。 另一方面,如果員工是開發人員,則不應允許訪問共享文件夾。 那就是我們需要做某種保護。 在這種情況下,保護代理可以派上用場。
我們在這里可以做的是,在員工和共享計算機之間,我們需要引入文件夾代理。 這個文件夾代理可以做的是,它會檢查員工的角色是經理還是首席執行官,然后允許員工訪問共享文件夾并執行讀寫操作。 另一方面,如果員工角色是 Developer 那么它會說你沒有權限訪問這個文件夾。 保護邏輯我們可以寫在文件夾代理中。
現在,我希望您了解代理設計模式。 那么,讓我們一步步在C#中實現代理設計模式吧。
第一步:創建員工類
創建一個名為 Employee.cs 的類文件,然后將以下代碼復制并粘貼到其中。
Step2:創建主體
創建一個名為 ISharedFolder 的接口,然后將以下代碼復制并粘貼到其中。 該接口定義了將由 RealSubject 和 Proxy 類實現的常用方法。
第三步:創建真實對象
創建一個名為 SharedFolder.cs 的類文件,然后將以下代碼復制并粘貼到其中。 此類實現主體 (ISharedFolder) 接口。
第四步:創建代理對象
創建一個名為 SharedFolderProxy.cs 的類文件,然后將以下代碼復制并粘貼到其中。 此類還實現了 Subject (ISharedFolder) 接口,并且它還持有對真實對象的引用。
Step5:客戶端代碼
請修改 Main 方法,如下所示。
了解代理設計模式類圖:
為了理解 C# 中代理設計模式的類圖,請看下圖。
如上圖所示,代理設計模式涉及三個參與者。 它們如下:
- 主體(ISharedFolder): 這是一個定義將由 RealSubject 和 Proxy 類實現的成員的接口,以便可以在任何需要 RealSubject 的地方使用 Proxy。 在我們的示例中,它是 ISharedFolder 接口。
- 真實對象(SharedFolder): 這是一個我們希望通過使用代理類來更高效地使用的類。 在我們的示例中,它是 SharedFolder 類。
- 代理(SharedFolderProxy): 這是一個持有對 RealSubject 類的引用的類,可以根據需要訪問 RealSubjectr 類成員。 它必須實現與 RealSubject 相同的接口,以便兩者可以互換使用。 在我們的示例中,它是 SharedFolderProxy 類。
何時在 C# 實時應用程序中使用代理設計模式?
以下是您可以在 C# 實時應用程序中使用代理設計模式的一些實時場景。
- 添加對現有對象的安全訪問。 代理將確定客戶端是否可以訪問感興趣的對象。
- 簡化復雜對象的 API。 代理可以提供一個簡單的 API,這樣客戶端代碼就不必處理感興趣對象的復雜性。
- 為 Web 服務或 REST 資源等遠程資源提供接口。
- 通過要求遠程資源在訪問資源之前盡快開始操作來協調對遠程資源的昂貴操作。
- 在不更改現有類代碼的情況下向現有類添加線程安全功能。
C# 中的代理設計模式實時示例 – 虛擬代理
虛擬代理是創建成本高昂的對象的占位符。 真正的對象只有在客戶第一次請求或訪問一個對象時才被創建。 讓我們通過一個實時示例來理解這一點。 請看下面的圖片。 在右側,您可以看到系統A,它有一個 200 MB 的圖像(老虎圖像)。 在左側,您可以看到客戶端。 在客戶端和系統A 之間,有充當虛擬代理的系統B。
比方說,客戶端第一次向系統 B(虛擬代理)發送請求以顯示老虎圖像。 虛擬代理(即系統 B)要做的是,首先它會檢查虛擬代理中是否存在真實圖像對象。 如果真實圖像對象不存在,那么在第 1 步中它將創建真實圖像對象并從磁盤加載圖像,在第 2 步中它將調用真實圖像對象上的顯示圖像方法。 虛擬代理還保存在步驟 1 中創建的真實圖像對象。 第 1 步,即創建真實圖像對象并從磁盤加載圖像是一項昂貴的操作。
假設客戶端第二次向虛擬代理發出相同的請求以顯示老虎圖像。 現在,虛擬代理要做的是,檢查實像對象是否存在,它發現虛擬代理中存在實像對象(這是因為在第一個請求中,虛擬代理持有真實- 圖像對象)。 所以,虛擬代理要做的是,它不會執行第 1 步,即它不會創建真實圖像對象并從磁盤加載圖像。 相反,它將使用現有的真實圖像對象并調用 Display Image 方法,即 step2。 所以,通過這種方式,使用代理設計模式,我們可以避免一次又一次地創建一個昂貴的對象。
類圖:
請看下圖:
C#中代理設計模式實時實例的實現:
讓我們使用代理設計模式逐步實現上述實時示例。 眾所周知,代理設計模式涉及三個組件,例如主題、真實對象和代理對象。 讓我們一一實現上面的組件。
第一步:創建主體
這將是一個接口。 因此,創建一個名為 IImage 的接口,然后將以下代碼復制并粘貼到其中。 該接口提供將由真實對象和代理對象具體類實現的功能。 在我們的示例中,接口定義了一種方法,即 DisplayImage
步驟2:創建真實對象
這將是一個具體類,此類實現 IImage 接口并提供 DisplayImage 方法的實現。 因此,創建一個名為 RealImage.cs 的類文件,然后將以下代碼復制并粘貼到其中。 RealImage 類的這個構造函數將文件名作為參數,然后從磁盤加載文件。
注意:這里的對象創建過程中有一個昂貴的操作。 這是因為在創建對象時,它將從磁盤加載圖像。 LoadImageFromDisk 方法用于從磁盤加載圖像。 DisplayImage 方法只是用來顯示圖像。
第三步:創建代理
這將是一個具體類,它還實現 IImage 接口并提供 DisplayImage 方法的實現。 因此,創建一個名為 ProxyObject.cs 的類文件,然后將以下代碼復制并粘貼到其中。 作為 DisplayImage 方法的一部分,首先,我們檢查 realImage 實例是否為 null。 如果為 null,則我們將創建實例,然后在 realImage 實例上調用 DisplayImage 方法。 另一方面,如果 realImage 實例不為 null,則它不會創建該實例,而是使用現有的 realImage 實例來調用 DisplayImage 方法。
第四步:客戶端
請修改 Main 方法,如下所示。 首先,我們創建ProxyImage對象來顯示老虎圖像,然后調用3次DisplayImage方法。 在這種情況下,第一次調用 DisplayImage 方法將創建 RealImage 實例,因此它將從磁盤加載圖像。 但是從第二次調用 DisplayImage 方法開始,它將使用現有的 RealImage 實例,因此不會從磁盤加載圖像。 這個過程對于第二次創建代理對象以顯示獅子圖像也是相同的。