結構型模式描述如何將類或對象按某種布局組成更大的結構。它分為類結構型模式和對象結構型模式,前者采用繼承機制來組織接口和類,后者釆用組合或聚合來組合對象。
由于組合關系或聚合關系比繼承關系耦合度低,滿足“合成復用原則”,所以對象結構型模式比類結構型模式具有更大的靈活性。
結構型模式分為以下 7 種:
- 代理模式
- 適配器模式
- 裝飾者模式
- 橋接模式
- 外觀模式
- 組合模式
- 享元模式
5.1 代理模式
5.1.1 概述
由于某些原因需要給某對象提供一個代理以控制對該對象的訪問。這時,訪問對象不適合或者不能直接引用目標對象,代理對象作為訪問對象和目標對象之間的中介。
Java中的代理按照代理類生成時機不同又分為靜態代理和動態代理。靜態代理代理類在編譯期就生成,而動態代理代理類則是在Java運行時動態生成。動態代理又有JDK代理和CGLib代理兩種。
5.1.2 結構
代理(Proxy)模式分為三種角色:
- 抽象主題(Subject)類: 通過接口或抽象類聲明真實主題和代理對象實現的業務方法。(規范)
- 真實主題(Real Subject)類: 實現了抽象主題中的具體業務,是代理對象所代表的真實對象,是最終要引用的對象。
- 代理(Proxy)類 : 提供了與真實主題相同的接口,其內部含有對真實主題的引用,它可以訪問、控制或擴展真實主題的功能。
5.1.3 靜態代理
如果要買火車票的話,需要去火車站買票,坐車到火車站,排隊等一系列的操作,顯然比較麻煩。而火車站在多個地方都有代售點,我們去代售點買票就方便很多了。這個例子其實就是典型的代理模式,火車站是目標對象,代售點是代理對象。類圖如下:
代理類需要對火車站買票方法進行聚合
代碼如下:
//賣票接口
interface SellTickets {void sell();
}
//火車站 火車站具有賣票功能,所以需要實現SellTickets接口
class TrainStation implements SellTickets {public void sell() {System.out.println("火車站賣票");}
}
//代售點
class ProxyPoint implements SellTickets {private TrainStation station = new TrainStation();public void sell() {System.out.println("代理點收取一些服務費用");station.sell();}
}
//測試類
public class Client {public static void main(String[] args) {ProxyPoint pp = new ProxyPoint();pp.sell();}
}
從上面代碼中可以看出測試類直接訪問的是ProxyPoint類對象,也就是說ProxyPoint作為訪問對象和目標對象的中介。同時也對sell方法進行了增強(代理點收取一些服務費用)。
代理點收取一些服務費用
火車站賣票
5.1.4 JDK動態代理
接下來我們使用動態代理實現上面案例,先說說JDK提供的動態代理。Java中提供了一個動態代理類Proxy,Proxy并不是我們上述所說的代理對象的類,而是提供了一個創建代理對象的靜態方法(newProxyInstance方法)來獲取代理對象。
代碼如下:
nterface SellTickets {void sell();
}//火車站 火車站具有賣票功能,所以需要實現SellTickets接口
class TrainStation implements SellTickets {public void sell() {System.out.println("火車站賣票");}
}//獲取代理對象的工廠類
class ProxyFactory {private TrainStation station = new TrainStation();public SellTickets getProxyObject() {//使用Proxy獲取代理對象/*newProxyInstance()方法參數說明:ClassLoader loader : 類加載器,用于加載代理類,使用真實對象的類加載器即可Class<?>[] interfaces : 真實對象所實現的接口,代理模式真實對象和代理對象實現相同的接口InvocationHandler h : 代理對象的調用處理程序*/SellTickets sellTickets = (SellTickets) Proxy.newProxyInstance(station.getClass().getClassLoader(),station.getClass().getInterfaces(),new InvocationHandler() {/*InvocationHandler中invoke方法參數說明:proxy : 代理對象method : 對應于在代理對象上調用的接口方法的 Method 實例args : 代理對象調用接口方法時傳遞的實際參數*/public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {System.out.println("代理點收取一些服務費用(JDK動態代理方式)");//執行真實對象Object result = method.invoke(station, args);return result;}});return sellTickets;}
}//測試類
public class Client {public static void main(String[] args) {//獲取代理對象ProxyFactory factory = new ProxyFactory();SellTickets proxyObject = factory.getProxyObject();proxyObject.sell();}
}
代理點收取一些服務費用(JDK動態代理方式)
火車站賣票
使用了動態代理,我們思考下面問題:
-
ProxyFactory是代理類嗎?
ProxyFactory不是代理模式中所說的代理類,是提供的一個工廠來獲取代理對象。而代理類是程序在運行過程中動態的在內存中生成的類。通過阿里巴巴開源的 Java 診斷工具(Arthas【阿爾薩斯】)查看代理類的結構:
package com.sun.proxy; ? import com.itheima.proxy.dynamic.jdk.SellTickets; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; import java.lang.reflect.UndeclaredThrowableException; ? public final class $Proxy0 extends Proxy implements SellTickets {private static Method m1;private static Method m2;private static Method m3;private static Method m0; ?public $Proxy0(InvocationHandler invocationHandler) {super(invocationHandler);} ?static {try {m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object"));m2 = Class.forName("java.lang.Object").getMethod("toString", new Class[0]);m3 = Class.forName("com.itheima.proxy.dynamic.jdk.SellTickets").getMethod("sell", new Class[0]);m0 = Class.forName("java.lang.Object").getMethod("hashCode", new Class[0]);return;}catch (NoSuchMethodException noSuchMethodException) {throw new NoSuchMethodError(noSuchMethodException.getMessage());}catch (ClassNotFoundException classNotFoundException) {throw new NoClassDefFoundError(classNotFoundException.getMessage());}} ?public final boolean equals(Object object) {try {return (Boolean)this.h.invoke(this, m1, new Object[]{object});}catch (Error | RuntimeException throwable) {throw throwable;}catch (Throwable throwable) {throw new UndeclaredThrowableException(throwable);}} ?public final String toString() {try {return (String)this.h.invoke(this, m2, null);}catch (Error | RuntimeException throwable) {throw throwable;}catch (Throwable throwable) {throw new UndeclaredThrowableException(throwable);}} ?public final int hashCode() {try {return (Integer)this.h.invoke(this, m0, null);}catch (Error | RuntimeException throwable) {throw throwable;}catch (Throwable throwable) {throw new UndeclaredThrowableException(throwable);}} ?public final void sell() {try {this.h.invoke(this, m3, null);return;}catch (Error | RuntimeException throwable) {throw throwable;}catch (Throwable throwable) {throw new UndeclaredThrowableException(throwable);}} }
從上面的類中,我們可以看到以下幾個信息:
- 代理類($Proxy0)實現了SellTickets。這也就印證了我們之前說的真實類和代理類實現同樣的接口。
- 代理類($Proxy0)將我們提供了的匿名內部類對象傳遞給了父類。
-
動態代理的執行流程是什么樣?
下面是摘取的重點代碼:
//程序運行過程中動態生成的代理類 public final class $Proxy0 extends Proxy implements SellTickets {private static Method m3; ?public $Proxy0(InvocationHandler invocationHandler) {super(invocationHandler);} ?static {m3 = Class.forName("com.itheima.proxy.dynamic.jdk.SellTickets").getMethod("sell", new Class[0]);} ?public final void sell() {