設計模式-代理模式(Proxy Model)

来源:http://www.cnblogs.com/vincentzh/archive/2016/10/23/5988145.html
-Advertisement-
Play Games

本文主要論述設計模式中代理模式的原理和代碼實現,其中包含靜態代理和動態代理。 ...


 文 / vincentzh 

原文連接:http://www.cnblogs.com/vincentzh/p/5988145.html 

目錄

  1. 1、寫在前面
  2. 2、概述
  3. 3、目的
  4. 4、結構組成
  5. 5、實現
    1. 5.1 靜態代理
    2. 5.2 動態代理
  6. 6、總結

1、寫在前面

  這次寫代理模式,代理模式裡面的動態代理(Dynamic Proxy)邏輯稍顯混亂,不太容易理解,本章節會將代理模式裡面的靜態代理(Static Proxy)和動態代理(Dynamic Proxy)結合對比理解,其中動態代理的實現需要具備 Java 的反射(Reflection)知識,原理方面會結合實例和靜態代理的對比儘量說清楚。

2、概述

  在某些情況下,一個客戶不想或者不能直接引用一個對象,代理對象就再客戶端和被代理對象之間起到中介的作用。就好比你在北京租房,初來乍到,人生地不熟,找房子遍地都是中介,想找房東可沒那麼容易(基本算得上是找不到房東直租的)。問題來了,找不到房東直租,但房子又是一切的基礎,so....走中介,房東能租房,中介也能租房,不過是你通過中介去將房東的房子租給自己。OK,這就是一個活生生的代理模式的例子,相必在外漂泊的小年輕們都感同身受吧。

  再說說代理模式的分類,代理模式主要分為兩類:靜態代理(Static Proxy)動態代理(Dynamic Proxy)

 靜態代理:一個被代理的真實對象對應一個代理,相當於一個租房中介只代理一個房東。弊端很明顯,這樣下去中介早餓死了!!!並且中介公司管理這麼龐大的中介團隊早晚逗得垮掉。反應到代碼裡面就是:代理類急劇增加,靈活性降低,增加了代碼的複雜度。

   動態代理:動態代理主要是去解決靜態代理存在的問題(及一個代理對應一個被代理對象),現實中也是這樣,不可能有中介只做一個房東的生意,一個中介手裡n多房子供你選擇,3人間、6人間、隔斷、小兩居等各種房源。反應到代碼裡面:代理類只有一個,根據客戶需求動態的去改變代理的真實對象,增加了代碼的靈活性,降低了代碼的複雜性。

3、目的

  代理模式的目的在於:為其他對象提供一種代理的方式控制被代理對象。

4、結構組成

  代理模式主要涉及到三個角色:抽象角色、代理角色、真實角色(被代理的角色)。

 抽象角色:聲明真實對象和代理對象的共同介面。即真實對象和代理對象共同要實現的行為動作(好比房東和中介都要能夠實現租房的行為,都能把房子租給你)。

 代理角色:代理角色內部含有對真實角色的引用,從而可以去操作真實對象,同時代理對象提供與真實對象的介面,以便在任何時候都能代替真實對象。同時,代理對象在執行真實對象的操作時,也能附加它自己的操作,相當於對真實對象的封裝(可以理解為中介在執行將房東的房子租給你這一操作時,可以向你收取中介費,還可以在退房的時候扣你押金這類房東不具有的操作)。

 真實角色:代理角色所代理的對象,亦即我們最終要引用的對象。

5、實現

  5.1 靜態代理

    靜態代理主要涉及到的三個角色關係圖如下:

    代理角色和真實角色都要實現同一個操作行為介面(或繼承同一個抽象類),並且代理角色依賴於真實角色,因為代理角色內部有對真實角色的引用,代理在操作真實角色去執行動作時,必須要知道是哪  個真實角色。

    

    下麵是靜態代理的實現:

      聲明抽象角色和代理角色實現的共同介面。     

1 package com.cnblogs.vincentzh.staticproxy;
2 //聲明出租房屋介面
3 public interface RentOut
4 {
5     public void rentOut();
6 }

 

   創建真實角色,實現租房介面。

 1 package com.cnblogs.vincentzh.staticproxy;
 2 // 創建房主類(真實角色)
 3 public class HouseHolder implements RentOut
 4 {    
 5     @Override
 6     public void rent()
 7     {
 8         System.out.println("I’m renting the house!");
 9     }
10 }     

   

   創建代理角色,實現與真實角色相同的介面

 1 package com.cnblogs.vincentzh.staticproxy;
 2 // 創建代理(中介)角色,與房東實現相同介面
 3 public class Proxy implements RentOut
 4 {
 5     private HouseHolder houseHolder; // 代理角色內部含有真實角色的引用
 6         
 7     // 重寫租房方法,添加中介操作
 8     @Override
 9     public void rent()
10     {
11         this.preRentOut(); // 代理對象添加自己的操作
12         
13         if(null == houseHolder)
14         {
15             houseHolder = new HouseHolder();
16         }
17         houseHolder.rent();
18         
19         this.postRentOut(); // 代理對象添加自己的操作
20     }
21     
22     // 中介操作,租房前收中介費
23     public void preRentOut()
24     {
25         System.out.println("I need more money!");
26     }
27     
28     // 中介操作,租房後扣押金
29     public void postRentOut()
30     {
31         System.out.println("I will deduct some money!");
32     }
33 }

  

      創建真實環境類,實現代理功能。

      客戶對象並不知道被代理的真實對象,只知道自己需要進行某項操作,並且某個代理能幫助自己完成這一操作。(即客戶要租房子,並不知道要從哪個房東手裡租房,只知道中介能幫助自己租到房子,至於房子從哪個房東那裡租的,就是中介(代理)的事兒了,客戶並不知道,也不需要直到)。

 1 package com.cnblogs.vincentzh.staticproxy;
 2 // 租房客戶類
 3 public class Client
 4 {
 5     /**
 6      * @param args
 7      */
 8     public static void main(String[] args)
 9     {
10         // 涉世未深的小年輕要租房,苦於聯繫不到房東,只能通過中介
11         RentOut rentOut = new Proxy();
12         rentOut.rent();
13     }
14 }

  

  運行程式,客戶類通過代理訪問真實角色,不僅實現真實角色的操作行為,同時也添加了代理自己的操作行為。

 

 

  靜態代理的弊端就在於:每當有一個真實對象需要被代理,就需要創建一個新的代理類,因為代理類內含有對真實對象的引用,代理類需要與真實對象一一對應。這樣,當需要代理的真實對象比較多時,代理類就會急劇增加,增加了代碼的複雜性,使代碼變得尤其繁重,不易維護。那麼動態代理就很好的解決了這一問題。

  5.2 動態代理

    動態代理解決了靜態代理存在的弊端,實現了多個被代理真實對象僅由一個代理類代理即可。

    1)創建兩個行為介面,分別實現 rent、sale 不同操作的行為;

 

1 package com.cnblogs.vincentzh.dynamicproxy;
2 // 創建租房介面
3 public interface Rent
4 {
5     public void rent();
6 }

 

1 package com.cnblogs.vincentzh.dynamicproxy;
2 // 創建售賣介面
3 public interface Sale
4 {
5     public void sale();
6 }

    

    2)創建兩個需要被代理的真實對象類,且他們分別實現了含有不同操作行為的介面;

 1 package com.cnblogs.vincentzh.dynamicproxy;
 2 // 創建房主類(真實角色),實現租房介面
 3 public class HouseHolder implements Rent
 4 {
 5     @Override
 6     public void rent()
 7     {
 8         System.out.println("HouseHolder: I’m renting the house!");        
 9     }
10 }
 1 package com.cnblogs.vincentzh.dynamicproxy;
 2 // 創建商人類(真實對象),實現售賣介面
 3 public class BusinessMan implements Sale
 4 {
 5     @Override
 6     public void sale()
 7     {
 8         System.out.println("BusinessMan: I'm salling something!");
 9     }
10 }

  

    3)創建動態代理類,只需要一個代理類,就能夠實現對 n 個需代理對象的代理,從而解決了靜態代理中代理與真實對象一一對應導致類的數目急劇增加的問題。

 1 package com.cnblogs.vincentzh.dynamicproxy;
 2 // 創建動態代理類,實現代碼運行過程中對各種真實對象的動態代理
 3 import java.lang.reflect.InvocationHandler;
 4 import java.lang.reflect.Method;
 5 
 6 public class DynamicProxy implements InvocationHandler
 7 {
 8     private Object realSubject;
 9     
10     public DynamicProxy(Object realSubject)
11     {
12         this.realSubject = realSubject;
13     }
14     
15     public void setRealSubject(Object realSubject)
16     {
17         this.realSubject = realSubject;
18     }
19 
20 
21     @Override
22     // 實現InvocationHandler介面的 invoke 方法,當代理類調用真實對象的方法時,
23     // 將直接尋找執行 invoke 方法。
24     public Object invoke(Object proxy, Method method, Object[] args)
25             throws Throwable
26     {
27         this.preRent(); // 執行代理自己添加的行為操作
28         
29         method.invoke(realSubject, args); // 以反射(reflection)的形式引用真實對象的方法
30         
31         this.postRent(); // 執行代理自己添加的行為操作
32         return null;
33     }
34     
35     // 代理類自行添加的行為
36     public void preRent()
37     {
38         System.out.println("I need more money!");
39     }
40     
41     // 代理類自行添加的行為
42     public void postRent()
43     {
44         System.out.println("I will deduct some money!");
45     }
46 }

 

    4)客戶調用類,只需通過改變被代理的真實對象,就能直接實現被代理的對象的更換,就不需要再去為 BusinessMan 類創建一個新的代理類了。BusinessMan類和HouseHolder類公用一個DynamicProxy動態代理類,但在做代理時,卻是動態生成兩個不同的代理實例(Proxy Instance),這就是所謂的動態代理。

 1 package com.cnblogs.vincentzh.dynamicproxy;
 2 //創建客戶類
 3 import java.lang.reflect.Proxy;
 4 
 5 public class Client
 6 {
 7     /**
 8      * @param args
 9      */
10     public static void main(String[] args)
11     {
12         HouseHolder houseHolder = new HouseHolder();
13         DynamicProxy handler = new DynamicProxy(houseHolder); // 生成 HouseHolder 的代理
14         
15         // 動態生成代理實例(HouseHold代理實例),代理支持的介面由初始化參數(第二個)指定,代理實例處理操作所調用的 handler 由第三個參數指定
16         Rent rent = (Rent)Proxy.newProxyInstance(HouseHolder.class.getClassLoader(), HouseHolder.class.getInterfaces(), handler);
17         rent.rent(); // 執行客戶需要進行的行為操作,動態生成的代理實例直接調用指定 handler 的 invoke 方法
18         
19         System.out.println("----------------------------------");
20         
21         BusinessMan businessMan = new BusinessMan();
22         handler.setRealSubject(businessMan); // 為代理更換引用的真實對象(即原本被代理的 HouseHolder 被更換為了 BusinessMan)
23         // 動態生成代理實例(BusinessMan代理實例)
24         // 註:代理實例實在代碼執行過程中動態執行的
25         Sale sale = (Sale)Proxy.newProxyInstance(BusinessMan.class.getClassLoader(), BusinessMan.class.getInterfaces(), handler);
26         sale.sale();        
27     }
28 }

 

  執行結果:

6、總結

  代理模式解決了不能直接操作真實對象的問題,其中的動態代理更是使代理模式的使用簡化不少。若使用靜態代理,那麼當有不用的真實對象(分別實現了不同的操作介面),我們只能去為被代理的真實對象重新生成一個代理類,而動態代理就解決了這一問題,用一個代理類,解決了所有真實對象的代理。通俗點將,這是個全能的代理,既能幹租房中介的活,還能幹買菜中介的活,也能幹媒人的活,聽起來貌似很牛逼的樣子。


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 當比較簡單類型時(如String int float bool),判斷的是"相等 && 類型一樣" 比較對象時,判斷的是"是否指向同一個對象" ...
  • 深度優先搜索 # Definition for a binary tree node.# class TreeNode:# def __init__(self, x):# self.val = x# self.left = None# self.right = Noneclass Solution: ...
  • OJ搭建好了後,我們要熟悉一下OJ項目下的文件及文件夾。 首先,安裝好的OJ是在目錄var/www/html下。 html下的php文件 這些php文件都是些主要跳轉頁面。 admin文件夾 登錄管理員賬號後管理的管理界面 bootstrap文件夾 css樣式和圖片,如果要修改某些頁面的小地方請到w ...
  • 本節介紹TreeSet,相比HashSet,它有什麼不同?除了Set介面,它實現的SortedSet和NavigatableSet介面有哪些方法?它內部是如何實現的?... ...
  • ...
  • day03員工的添加,修改,刪除 修改功能 思路 : 點擊修改員工數據之後,跳轉到單行文本,查詢要修改的員工id的全部信息,主鍵id設置為readonly,其餘的都可以修改, 修改之後,提交按鈕,提交之後跳轉到全部員工界面 修改界面 : 部分代碼 : 刪除員工信息 那就相對簡單了,點擊刪除之後,帶上 ...
  • 轉載請標明出處:http://blog.csdn.net/lmj623565791/article/details/24269409 裝飾者模式:若要擴展功能,裝飾者提供了比集成更有彈性的替代方案,動態地將責任附加到對象上。 先簡單描述下裝飾者模式發揮作用的地方,當我們設計好了一個類,我們需要給這個 ...
  • 單件模式,也稱單例模式,用以創建獨一無二的、只能有一個實例的對象。 單件模式的類圖是所有模式的類圖中最簡單的——只有一個類。儘管從類設計的視角來看單件模式很簡單,但是實現上還是會遇到一些問題,本文著重對這一點來進行分析解決。 最簡單的單件模式的實現,代碼如下: 結合以上的代碼,對單件模式進行簡單的闡 ...
一周排行
    -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.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...