最近有時間,學習了一下設計模式,發現了一個問題,代理模式(靜態代理)在寫法和結構上,基本和裝飾器是一樣的。
由此引發了對這兩者的真正區別的思考,網上搜索了許許多多的答案(雖然都有一定的道理,個人覺得都沒有說到真正的重點) :
1 . 有的人說是結構上不同,代理和真實對象之間的的關系通常在編譯時就已經確定了,而裝飾器能夠在運行時遞歸地被構造(我個人完全反對這種說法);
2 . 裝飾器模式為了增強功能;而代理模式是為了加以控制,代理類對被代理的對象有控制權,決定其執行或者不執行。(大名鼎鼎的菜鳥教程這樣解釋);
3 . 甚至還有人說裝飾器模式用於添加新方法;而代理模式則用於增強原方法(那為什么叫代理?)。
代理模式(靜態)與裝飾者雖然在結構上基本上一模一樣,但兩者卻有真正區別,我認為是 : 目的不一樣,關注的重心不一樣。
代理模式目的 : 讓原有對象被代理,我們的目的是讓使用者盡可能的感受不到原有對象,原有對象的行為或額外的動作交由代理對象完成。(完成代理模式的真正意義)
裝飾器模式目的 : 讓原有對象被增強,我們的目的通常是得到由原有對象被增強后的裝飾器對象行為。(完成裝飾器模式的真正意義)
代理模式關注重心 : 主要功能不變,代理對象只是幫忙代理或稍加擴展原有對象的行為,功能上主要關心原有對象所具有的行為。(最終主要功能仍然由原有對象決定)
裝飾器模式關注重心 : 主要功能增強,使用裝飾器目的就是為了增強,功能上更關心裝飾增加后的行為。(最終主要功能由裝飾對象決定)
靜態代理
靜態代理的角色分為 : 抽象行為角色,委托人,代理人。基本寫法如下 :
抽象行為角色 : 是委托人和代理人的共同接口。這里我們叫它抽象主題(Subject) :
package name.ealen.proxy.designPattern.staticProxy; /** * Created by EalenXie on 2018/11/2 10:16. */ public interface Subject { public void operation(); }
委托人 : 也就是我上面一直說的原有對象,真正被代理的對象,也叫做代理元。這里我們叫它真實主題 (RealSubject):
package name.ealen.proxy.designPattern.staticProxy; /** * Created by EalenXie on 2018/11/2 10:17. */ public class RealSubject implements Subject { @Override public void operation() { System.out.println("真實對象 : 重要操作"); } }
代理人 : 代理角色,由它去代理原有對象。它包含被代理對象的引用,這里叫它代理主題(ProxySubject):
package name.ealen.proxy.designPattern.staticProxy; /** * Created by EalenXie on 2018/11/2 10:18. */ public class ProxySubject implements Subject { private Subject subject; public ProxySubject(Subject subject) { this.subject = subject; } /** * 目的 : 代理真實對象完成方法調用,代理可以進行相對不重要的行為擴展 */ @Override public void operation() { before(); subject.operation(); after(); } private void after() { System.out.println("代理人 : 真實對象的操作完成了"); } private void before() { System.out.println("代理人 : 開始完成真實對象的操作"); } }
測試代碼 :
/** * 靜態代理 */ @Test public void staticProxy() { Subject realSubject = new RealSubject(); //一個真實對象 Subject proxy = new ProxySubject(realSubject); //一個代理人,指定要代理的真實對象,類型只能是Subject及其子類 proxy.operation(); //整個操作由代理人幫真實對象完成,代理人還做了操作說明 }
結果如下 :
動態代理
動態代理主要依賴Java反射機制實現,基本寫法如下 :
抽象行為角色 : 是委托人和代理人的共同接口。這里我們叫它抽象主題(DynamicSubject) :
package name.ealen.proxy.designPattern.dynamicProxy; /** * Created by EalenXie on 2018/11/2 15:35. */ public interface DynamicSubject { public void operation() ; }
一個被代理對象 ,這里叫他真實對象(DynamicRealSubject) :
package name.ealen.proxy.designPattern.dynamicProxy; /** * Created by EalenXie on 2018/11/2 12:56. */ public class DynamicRealSubject implements DynamicSubject { @Override public void operation() { System.out.println("真實對象 : 重要操作"); } }
代理的調用處理類(ProxyHandler) ,主要實現反射的接口 InvocationHandler ,這里可以看出該處理類設計上和被代理對象並沒有任何的直接聯系 :
package name.ealen.proxy.designPattern.dynamicProxy; import java.lang.reflect.InvocationHandler; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; /** * Created by EalenXie on 2018/11/2 12:57. * 代理的調用處理類 */ public class ProxyHandler implements InvocationHandler { private Object realSubject; //指定被代理的真實對象 public ProxyHandler(Object realSubject) { this.realSubject = realSubject; } @Override public Object invoke(Object proxy, Method method, Object[] args) throws InvocationTargetException, IllegalAccessException { before(); //自定義邏輯 Object result = method.invoke(realSubject, args); //調用真實對象的操作 after(); //自定義邏輯 return result; } private void after() { System.out.println("代理人 : 真實對象的操作完成了"); } private void before() { System.out.println("代理人 : 開始完成真實對象的操作"); } }
那么如何得到我們的代理對象呢?答案是基於反射類Proxy,既然是動態代理,那么代理的調用處理類,是可以代理任何類型的對象(但動態代理規定該對象必須實現一個或多個接口)。請看如下測試類 :
/** * 動態代理,基於反射類實現 */ @Test public void dynamicProxy() { //一個真實對象 DynamicSubject subject = new DynamicRealSubject(); //動態代理處理邏輯 ,基於反射類InvocationHandler實現,指定要代理的真實對象,可以是任何類型,但必須實現一個或多個接口 InvocationHandler proxyHandler = new ProxyHandler(subject); //一個代理人,實例化基於反射類Proxy實現。 DynamicSubject proxy = (DynamicSubject) Proxy.newProxyInstance(DynamicSubject.class.getClassLoader(), subject.getClass().getInterfaces(), proxyHandler); //整個操作由代理人幫真實對象完成,代理人還做了操作說明 proxy.operation(); }
只需要在ProxyHandler中動態的傳入任何我們需要代理的對象(必須實現一個或多個接口),然后Proxy調用newProxyInstance,強轉即可得到我們的代理對象,結果如下 :