结构型模式描述如何将类或对象按某种布局组成更大的结构。它分为类结构型模式和对象结构型模式,前者采用继承机制来组织接口和类,后者釆用组合或聚合来组合对象。
由于组合关系或聚合关系比继承关系耦合度低,满足“合成复用原则”,所以对象结构型模式比类结构型模式具有更大的灵活性。
结构型模式分为以下 7 种:
代理模式
适配器模式
装饰者模式
桥接模式
外观模式
组合模式
享元模式
代理模式 概念 由于某些原因需要给某对象提供一个代理以控制对该对象的访问。这时,访问对象不适合或者不能直接引用目标对象,代理对象作为访问对象和目标对象之间的中介。
Java中的代理按照代理类生成时机不同又分为静态代理和动态代理。静态代理代理类在编译期就生成,而动态代理代理类则是在Java运行时动态生成。动态代理又有JDK代理和CGLib代理两种。
结构 代理(Proxy)模式分为三种角色:
抽象主题(Subject)类: 通过接口或抽象类声明真实主题和代理对象实现的业务方法。
真实主题(Real Subject)类: 实现了抽象主题中的具体业务,是代理对象所代表的真实对象,是最终要引用的对象。
代理(Proxy)类 : 提供了与真实主题相同的接口,其内部含有对真实主题的引用,它可以访问、控制或扩展真实主题的功能。
静态代理 如果要买火车票的话,需要去火车站买票,坐车到火车站,排队等一系列的操作,显然比较麻烦。而火车站在多个地方都有代售点,我们去代售点买票就方便很多了。这个例子就是典型的代理模式,火车站是目标对象,代售点是代理对象。
抽象主题
1 2 3 4 5 6 7 8 9 public interface SellTickets { void sell () ; }
真实主题
1 2 3 4 5 6 7 8 9 10 public class TrainStation implements SellTickets { @Override public void sell () { System.out.println("火车站卖票" ); } }
代理类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 public class ProxyPoint implements SellTickets { private TrainStation station = new TrainStation (); @Override public void sell () { System.out.println("进行收费" ); station.sell(); } }
测试
1 2 3 4 5 6 7 8 9 10 11 public class App { public static void main (String[] args) { ProxyPoint point = new ProxyPoint (); point.sell(); } }
动态代理 JDK动态代理 代理工厂
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 import java.lang.reflect.InvocationHandler;import java.lang.reflect.Method;import java.lang.reflect.Proxy;public class ProxyFactory { private TrainStation station = new TrainStation (); public SellTickets getProxyObject () { SellTickets proxyObject = (SellTickets)Proxy.newProxyInstance( station.getClass().getClassLoader(), station.getClass().getInterfaces(), new InvocationHandler () { @Override public Object invoke (Object proxy, Method method, Object[] args) throws Throwable { System.out.println("代售点收取服务费(JDK动态代理)" ); Object invoke = method.invoke(station, args); return invoke; } } ); return proxyObject; } }
代理类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 package com.sun.proxy;import com.xxx.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.xxx.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); } } }
测试
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 public class App { public static void main (String[] args) { ProxyPoint point = new ProxyPoint (); point.sell(); ProxyFactory factory = new ProxyFactory (); SellTickets proxyObject = factory.getProxyObject(); proxyObject.sell(); } }
执行流程如下:
1. 在测试类中通过代理对象调用sell()方法
2. 根据多态的特性,执行的是代理类($Proxy0)中的sell()方法
3. 代理类($Proxy0)中的sell()方法中又调用了InvocationHandler接口的子实现类对象的invoke方法
4. invoke方法通过反射执行了真实对象所属类(TrainStation)中的sell()方法
CGLIB动态代理 代理工厂
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 import net.sf.cglib.proxy.Enhancer;import net.sf.cglib.proxy.MethodInterceptor;import net.sf.cglib.proxy.MethodProxy;import java.lang.reflect.Method;public class CglibProxyFactory implements MethodInterceptor { TrainStation station = new TrainStation (); public TrainStation getProxyObject () { Enhancer enhancer = new Enhancer (); enhancer.setSuperclass(TrainStation.class); enhancer.setCallback(this ); TrainStation proxyObject = (TrainStation) enhancer.create(); return proxyObject; } @Override public Object intercept (Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable { System.out.println("代售点收取服务费(Cglib动态代理)" ); Object invoke = method.invoke(station, objects); return invoke; } }
代理类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 package com.xxx;import com.xxx.TrainStation;import java.lang.reflect.Method;import net.sf.cglib.core.ReflectUtils;import net.sf.cglib.core.Signature;import net.sf.cglib.proxy.Callback;import net.sf.cglib.proxy.Factory;import net.sf.cglib.proxy.MethodInterceptor;import net.sf.cglib.proxy.MethodProxy;public class TrainStation$$EnhancerByCGLIB$$f3f5c491 extends TrainStation implements Factory { private boolean CGLIB$BOUND; private static final ThreadLocal CGLIB$THREAD_CALLBACKS; private static final Callback[] CGLIB$STATIC_CALLBACKS; private MethodInterceptor CGLIB$CALLBACK_0; private static final Method CGLIB$sell$0 $Method; private static final MethodProxy CGLIB$sell$0 $Proxy; private static final Object[] CGLIB$emptyArgs; private static final Method CGLIB$finalize$1 $Method; private static final MethodProxy CGLIB$finalize$1 $Proxy; private static final Method CGLIB$equals$2 $Method; private static final MethodProxy CGLIB$equals$2 $Proxy; private static final Method CGLIB$toString$3 $Method; private static final MethodProxy CGLIB$toString$3 $Proxy; private static final Method CGLIB$hashCode$4 $Method; private static final MethodProxy CGLIB$hashCode$4 $Proxy; private static final Method CGLIB$clone$5 $Method; private static final MethodProxy CGLIB$clone$5 $Proxy; public TrainStation$$EnhancerByCGLIB$$f3f5c491() { TrainStation$$EnhancerByCGLIB$$f3f5c491 trainStation$$EnhancerByCGLIB$$f3f5c491 = this ; TrainStation$$EnhancerByCGLIB$$f3f5c491.CGLIB$BIND_CALLBACKS(trainStation$$EnhancerByCGLIB$$f3f5c491); } static { TrainStation$$EnhancerByCGLIB$$f3f5c491.CGLIB$STATICHOOK1(); } protected final void finalize () throws Throwable { MethodInterceptor methodInterceptor = this .CGLIB$CALLBACK_0; if (methodInterceptor == null ) { TrainStation$$EnhancerByCGLIB$$f3f5c491.CGLIB$BIND_CALLBACKS(this ); methodInterceptor = this .CGLIB$CALLBACK_0; } if (methodInterceptor != null ) { Object object = methodInterceptor.intercept(this , CGLIB$finalize$1 $Method, CGLIB$emptyArgs, CGLIB$finalize$1 $Proxy); return ; } super .finalize(); } public final boolean equals (Object object) { MethodInterceptor methodInterceptor = this .CGLIB$CALLBACK_0; if (methodInterceptor == null ) { TrainStation$$EnhancerByCGLIB$$f3f5c491.CGLIB$BIND_CALLBACKS(this ); methodInterceptor = this .CGLIB$CALLBACK_0; } if (methodInterceptor != null ) { Object object2 = methodInterceptor.intercept(this , CGLIB$equals$2 $Method, new Object []{object}, CGLIB$equals$2 $Proxy); return object2 == null ? false : (Boolean)object2; } return super .equals(object); } public final String toString () { MethodInterceptor methodInterceptor = this .CGLIB$CALLBACK_0; if (methodInterceptor == null ) { TrainStation$$EnhancerByCGLIB$$f3f5c491.CGLIB$BIND_CALLBACKS(this ); methodInterceptor = this .CGLIB$CALLBACK_0; } if (methodInterceptor != null ) { return (String)methodInterceptor.intercept(this , CGLIB$toString$3 $Method, CGLIB$emptyArgs, CGLIB$toString$3 $Proxy); } return super .toString(); } public final int hashCode () { MethodInterceptor methodInterceptor = this .CGLIB$CALLBACK_0; if (methodInterceptor == null ) { TrainStation$$EnhancerByCGLIB$$f3f5c491.CGLIB$BIND_CALLBACKS(this ); methodInterceptor = this .CGLIB$CALLBACK_0; } if (methodInterceptor != null ) { Object object = methodInterceptor.intercept(this , CGLIB$hashCode$4 $Method, CGLIB$emptyArgs, CGLIB$hashCode$4 $Proxy); return object == null ? 0 : ((Number)object).intValue(); } return super .hashCode(); } protected final Object clone () throws CloneNotSupportedException { MethodInterceptor methodInterceptor = this .CGLIB$CALLBACK_0; if (methodInterceptor == null ) { TrainStation$$EnhancerByCGLIB$$f3f5c491.CGLIB$BIND_CALLBACKS(this ); methodInterceptor = this .CGLIB$CALLBACK_0; } if (methodInterceptor != null ) { return methodInterceptor.intercept(this , CGLIB$clone$5 $Method, CGLIB$emptyArgs, CGLIB$clone$5 $Proxy); } return super .clone(); } public Object newInstance (Callback callback) { TrainStation$$EnhancerByCGLIB$$f3f5c491.CGLIB$SET_THREAD_CALLBACKS(new Callback []{callback}); TrainStation$$EnhancerByCGLIB$$f3f5c491 trainStation$$EnhancerByCGLIB$$f3f5c491 = new TrainStation$$EnhancerByCGLIB$$f3f5c491 (); TrainStation$$EnhancerByCGLIB$$f3f5c491.CGLIB$SET_THREAD_CALLBACKS(null ); return trainStation$$EnhancerByCGLIB$$f3f5c491; } public Object newInstance (Callback[] callbackArray) { TrainStation$$EnhancerByCGLIB$$f3f5c491.CGLIB$SET_THREAD_CALLBACKS(callbackArray); TrainStation$$EnhancerByCGLIB$$f3f5c491 trainStation$$EnhancerByCGLIB$$f3f5c491 = new TrainStation$$EnhancerByCGLIB$$f3f5c491 (); TrainStation$$EnhancerByCGLIB$$f3f5c491.CGLIB$SET_THREAD_CALLBACKS(null ); return trainStation$$EnhancerByCGLIB$$f3f5c491; } public Object newInstance (Class[] classArray, Object[] objectArray, Callback[] callbackArray) { TrainStation$$EnhancerByCGLIB$$f3f5c491 trainStation$$EnhancerByCGLIB$$f3f5c491; TrainStation$$EnhancerByCGLIB$$f3f5c491.CGLIB$SET_THREAD_CALLBACKS(callbackArray); Class[] classArray2 = classArray; switch (classArray.length) { case 0 : { trainStation$$EnhancerByCGLIB$$f3f5c491 = new TrainStation$$EnhancerByCGLIB$$f3f5c491 (); break ; } default : { throw new IllegalArgumentException ("Constructor not found" ); } } TrainStation$$EnhancerByCGLIB$$f3f5c491.CGLIB$SET_THREAD_CALLBACKS(null ); return trainStation$$EnhancerByCGLIB$$f3f5c491; } public void setCallback (int n, Callback callback) { switch (n) { case 0 : { this .CGLIB$CALLBACK_0 = (MethodInterceptor)callback; break ; } } } public final void sell () { MethodInterceptor methodInterceptor = this .CGLIB$CALLBACK_0; if (methodInterceptor == null ) { TrainStation$$EnhancerByCGLIB$$f3f5c491.CGLIB$BIND_CALLBACKS(this ); methodInterceptor = this .CGLIB$CALLBACK_0; } if (methodInterceptor != null ) { Object object = methodInterceptor.intercept(this , CGLIB$sell$0 $Method, CGLIB$emptyArgs, CGLIB$sell$0 $Proxy); return ; } super .sell(); } public void setCallbacks (Callback[] callbackArray) { Callback[] callbackArray2 = callbackArray; TrainStation$$EnhancerByCGLIB$$f3f5c491 trainStation$$EnhancerByCGLIB$$f3f5c491 = this ; this .CGLIB$CALLBACK_0 = (MethodInterceptor)callbackArray[0 ]; } public static void CGLIB$SET_STATIC_CALLBACKS(Callback[] callbackArray) { CGLIB$STATIC_CALLBACKS = callbackArray; } public static void CGLIB$SET_THREAD_CALLBACKS(Callback[] callbackArray) { CGLIB$THREAD_CALLBACKS.set(callbackArray); } public Callback[] getCallbacks() { TrainStation$$EnhancerByCGLIB$$f3f5c491.CGLIB$BIND_CALLBACKS(this ); TrainStation$$EnhancerByCGLIB$$f3f5c491 trainStation$$EnhancerByCGLIB$$f3f5c491 = this ; return new Callback []{this .CGLIB$CALLBACK_0}; } public Callback getCallback (int n) { MethodInterceptor methodInterceptor; TrainStation$$EnhancerByCGLIB$$f3f5c491.CGLIB$BIND_CALLBACKS(this ); switch (n) { case 0 : { methodInterceptor = this .CGLIB$CALLBACK_0; break ; } default : { methodInterceptor = null ; } } return methodInterceptor; } final void CGLIB$finalize$1 () throws Throwable { super .finalize(); } final void CGLIB$sell$0 () { super .sell(); } final boolean CGLIB$equals$2 (Object object) { return super .equals(object); } final int CGLIB$hashCode$4 () { return super .hashCode(); } final String CGLIB$toString$3 () { return super .toString(); } final Object CGLIB$clone$5 () throws CloneNotSupportedException { return super .clone(); } public static MethodProxy CGLIB$findMethodProxy(Signature signature) { String string = ((Object)signature).toString(); switch (string.hashCode()) { case -1574182249 : { if (!string.equals("finalize()V" )) break ; return CGLIB$finalize$1 $Proxy; } case -508378822 : { if (!string.equals("clone()Ljava/lang/Object;" )) break ; return CGLIB$clone$5 $Proxy; } case 1826985398 : { if (!string.equals("equals(Ljava/lang/Object;)Z" )) break ; return CGLIB$equals$2 $Proxy; } case 1913648695 : { if (!string.equals("toString()Ljava/lang/String;" )) break ; return CGLIB$toString$3 $Proxy; } case 1978249955 : { if (!string.equals("sell()V" )) break ; return CGLIB$sell$0 $Proxy; } case 1984935277 : { if (!string.equals("hashCode()I" )) break ; return CGLIB$hashCode$4 $Proxy; } } return null ; } static void CGLIB$STATICHOOK1() { CGLIB$THREAD_CALLBACKS = new ThreadLocal (); CGLIB$emptyArgs = new Object [0 ]; Class<?> clazz = Class.forName("com.xxx.TrainStation$$EnhancerByCGLIB$$f3f5c491" ); Class<?> clazz2 = Class.forName("com.xxx.TrainStation" ); CGLIB$sell$0 $Method = ReflectUtils.findMethods(new String []{"sell" , "()V" }, clazz2.getDeclaredMethods())[0 ]; CGLIB$sell$0 $Proxy = MethodProxy.create(clazz2, clazz, "()V" , "sell" , "CGLIB$sell$0" ); clazz2 = Class.forName("java.lang.Object" ); Method[] methodArray = ReflectUtils.findMethods(new String []{"finalize" , "()V" , "equals" , "(Ljava/lang/Object;)Z" , "toString" , "()Ljava/lang/String;" , "hashCode" , "()I" , "clone" , "()Ljava/lang/Object;" }, clazz2.getDeclaredMethods()); CGLIB$finalize$1 $Method = methodArray[0 ]; CGLIB$finalize$1 $Proxy = MethodProxy.create(clazz2, clazz, "()V" , "finalize" , "CGLIB$finalize$1" ); CGLIB$equals$2 $Method = methodArray[1 ]; CGLIB$equals$2 $Proxy = MethodProxy.create(clazz2, clazz, "(Ljava/lang/Object;)Z" , "equals" , "CGLIB$equals$2" ); CGLIB$toString$3 $Method = methodArray[2 ]; CGLIB$toString$3 $Proxy = MethodProxy.create(clazz2, clazz, "()Ljava/lang/String;" , "toString" , "CGLIB$toString$3" ); CGLIB$hashCode$4 $Method = methodArray[3 ]; CGLIB$hashCode$4 $Proxy = MethodProxy.create(clazz2, clazz, "()I" , "hashCode" , "CGLIB$hashCode$4" ); CGLIB$clone$5 $Method = methodArray[4 ]; CGLIB$clone$5 $Proxy = MethodProxy.create(clazz2, clazz, "()Ljava/lang/Object;" , "clone" , "CGLIB$clone$5" ); } private static final void CGLIB$BIND_CALLBACKS(Object object) { block2: { Object object2; block3: { TrainStation$$EnhancerByCGLIB$$f3f5c491 trainStation$$EnhancerByCGLIB$$f3f5c491 = (TrainStation$$EnhancerByCGLIB$$f3f5c491)object; if (trainStation$$EnhancerByCGLIB$$f3f5c491.CGLIB$BOUND) break block2; trainStation$$EnhancerByCGLIB$$f3f5c491.CGLIB$BOUND = true ; object2 = CGLIB$THREAD_CALLBACKS.get(); if (object2 != null ) break block3; object2 = CGLIB$STATIC_CALLBACKS; if (CGLIB$STATIC_CALLBACKS == null ) break block2; } trainStation$$EnhancerByCGLIB$$f3f5c491.CGLIB$CALLBACK_0 = (MethodInterceptor)((Callback[])object2)[0 ]; } } }
测试
1 2 3 4 5 6 7 8 9 10 11 12 public class App { public static void main (String[] args) { CglibProxyFactory factory1 = new CglibProxyFactory (); TrainStation proxyObject1 = factory1.getProxyObject(); proxyObject1.sell(); } }
三种代理的对比
jdk代理和CGLIB代理
使用CGLib实现动态代理,CGLib底层采用ASM字节码生成框架,使用字节码技术生成代理类,在JDK1.6之前比使用Java反射效率要高。唯一需要注意的是,CGLib不能对声明为final的类或者方法进行代理,因为CGLib原理是动态生成被代理类的子类。
在JDK1.6、JDK1.7、JDK1.8逐步对JDK动态代理优化之后,在调用次数较少的情况下,JDK代理效率高于CGLib代理效率,只有当进行大量调用的时候,JDK1.6和JDK1.7比CGLib代理效率低一点,但是到JDK1.8的时候,JDK代理效率高于CGLib代理。所以如果有接口使用JDK动态代理,如果没有接口使用CGLIB代理。
动态代理和静态代理
动态代理与静态代理相比较,最大的好处是接口中声明的所有方法都被转移到调用处理器一个集中的方法中处理(InvocationHandler.invoke)。这样,在接口方法数量比较多的时候,我们可以进行灵活处理,而不需要像静态代理那样每一个方法进行中转。
如果接口增加一个方法,静态代理模式除了所有实现类需要实现这个方法外,所有代理类也需要实现此方法。增加了代码维护的复杂度。而动态代理不会出现该问题
优缺点 优点
代理模式在客户端与目标对象之间起到一个中介作用和保护目标对象的作用;
代理对象可以扩展目标对象的功能;
代理模式能将客户端与目标对象分离,在一定程度上降低了系统的耦合度;
缺点 增加了系统的复杂度;
使用场景
远程(Remote)代理
本地服务通过网络请求远程服务。为了实现本地到远程的通信,我们需要实现网络通信,处理其中可能的异常。为良好的代码设计和可维护性,我们将网络通信部分隐藏起来,只暴露给本地服务一个接口,通过该接口即可访问远程服务提供的功能,而不必过多关心通信部分的细节。
防火墙(Firewall)代理
当你将浏览器配置成使用代理功能时,防火墙就将你的浏览器的请求转给互联网;当互联网返回响应时,代理服务器再把它转给你的浏览器。
保护(Protect or Access)代理
控制对一个对象的访问,如果需要,可以给不同的用户提供不同级别的使用权限。
适配器模式 概念 将一个类的接口转换成客户希望的另外一个接口,使得原本由于接口不兼容而不能一起工作的那些类能一起工作。
适配器模式分为类适配器模式和对象适配器模式,前者类之间的耦合度比后者高,且要求程序员了解现有组件库中的相关组件的内部结构,所以应用相对较少些。
结构 适配器模式(Adapter)包含以下主要角色:
目标(Target)接口:当前系统业务所期待的接口,它可以是抽象类或接口。
适配者(Adaptee)类:它是被访问和适配的现存组件库中的组件接口。
适配器(Adapter)类:它是一个转换器,通过继承或引用适配者的对象,把适配者接口转换成目标接口,让客户按目标接口的格式访问适配者。
类适配器模式
实现方式:定义一个适配器类来实现当前系统的业务接口,同时又继承现有组件库中已经存在的组件。
现有一台电脑只能读取SD卡,而要读取TF卡中的内容的话就需要使用到适配器模式。创建一个读卡器,将TF卡中的内容读取出来。
目标接口
1 2 3 4 5 6 7 8 9 10 public interface SDCard { public String readSD () ; public void writeSD (String msg) ; }
目标接口实现类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 public class SDCardImpl implements SDCard { @Override public String readSD () { String msg = "read SDCard" ; return msg; } @Override public void writeSD (String msg) { System.out.println("SDCard write msg:" +msg); } }
适配者
1 2 3 4 5 6 7 8 9 10 11 public interface TFCard { String readTF () ; void writeTF (String msg) ; }
适配者实现类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 public class TFCardImpl implements TFCard { @Override public String readTF () { String msg = "read TFCard" ; return msg; } @Override public void writeTF (String msg) { System.out.println("TFCard write msg:" +msg); } }
主调用类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 public class Computer { public String readSD (SDCard sd) { if (sd == null ){ throw new NullPointerException ("SDCard is not null" ); } return sd.readSD(); } }
测试
1 2 3 4 5 6 7 8 9 10 11 12 13 14 public class App { public static void main (String[] args) { Computer computer = new Computer (); System.out.println(computer.readSD(new SDCardImpl ())); Computer computer1 = new Computer (); SDAdapterTF sdAdapterTF = new SDAdapterTF (); System.out.println(computer1.readSD(sdAdapterTF)); } }
类适配器模式违背了合成复用原则。类适配器是客户类有一个接口规范的情况下可用,反之不可用。
对象适配器模式
实现方式:对象适配器模式可釆用将现有组件库中已经实现的组件引入适配器类中,该类同时实现当前系统的业务接口。
适配器
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 public class SDObjectAdapterTF implements SDCard { private TFCard tfCard; public SDObjectAdapterTF (TFCard tfCard) { this .tfCard = tfCard; } @Override public String readSD () { System.out.println("adapter read tf card" ); return tfCard.readTF(); } @Override public void writeSD (String msg) { System.out.println("adapter write tf card" ); tfCard.writeTF(msg); } }
注意:还有一个适配器模式是接口适配器模式。当不希望实现一个接口中所有的方法时,可以创建一个抽象类Adapter ,实现所有方法。而此时我们只需要继承该抽象类即可。
使用场景
以前开发的系统存在满足新系统功能需求的类,但其接口同新系统的接口不一致。
使用第三方提供的组件,但组件接口定义和自己要求的接口定义不同。
装饰者模式 概念 指在不改变现有对象结构的情况下,动态地给该对象增加一些职责(即增加其额外功能)的模式。
结构 装饰(Decorator)模式中的角色:
抽象构件(Component)角色 :定义一个抽象接口以规范准备接收附加责任的对象。
具体构件(Concrete Component)角色 :实现抽象构件,通过装饰角色为其添加一些职责。
抽象装饰(Decorator)角色 : 继承或实现抽象构件,并包含具体构件的实例,可以通过其子类扩展具体构件的功能。
具体装饰(ConcreteDecorator)角色 :实现抽象装饰的相关方法,并给具体构件对象添加附加的责任。
实现 快餐店有炒面、炒饭这些快餐,可以额外附加鸡蛋、火腿、培根这些配菜,当然加配菜需要额外加钱,每个配菜的价钱通常不太一样,那么计算总价就会显得比较麻烦。
抽象构件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 import lombok.AllArgsConstructor;import lombok.Data;import lombok.NoArgsConstructor;@Data @AllArgsConstructor @NoArgsConstructor public abstract class FastFood { private float price; private String desc; public abstract float cost () ; }
具体构件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 public class FriedRice extends FastFood { public FriedRice () { super (10 ,"炒饭" ); } @Override public float cost () { return getPrice(); } }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 public class FriedNoodles extends FastFood { public FriedNoodles () { super (12 ,"炒面" ); } @Override public float cost () { return getPrice(); } }
抽象装饰
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 public abstract class Garnish extends FastFood { private FastFood fastFood; public FastFood getFastFood () { return fastFood; } public void setFastFood (FastFood fastFood) { this .fastFood = fastFood; } public Garnish (FastFood fastFood, float price, String desc) { super (price,desc); this .fastFood = fastFood; } }
具体装饰
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 public class Egg extends Garnish { public Egg (FastFood fastFood) { super (fastFood,1 ,"鸡蛋" ); } public float cost () { return getPrice() + getFastFood().getPrice(); } @Override public String getDesc () { return super .getDesc() + getFastFood().getDesc(); } }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 public class Bacon extends Garnish { public Bacon (FastFood fastFood) { super (fastFood,2 ,"培根" ); } @Override public float cost () { return getPrice() + getFastFood().getPrice(); } @Override public String getDesc () { return super .getDesc() + getFastFood().getDesc(); } }
测试
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 public class App { public static void main (String[] args) { FastFood food = new FriedRice (); System.out.println(food.getDesc() + " " + food.cost() + "元" ); System.out.println("========" ); FastFood food1 = new FriedRice (); food1 = new Egg (food1); System.out.println(food1.getDesc() + " " + food1.cost() + "元" ); System.out.println("========" ); FastFood food2 = new FriedNoodles (); food2 = new Bacon (food2); System.out.println(food2.getDesc() + " " + food2.cost() + "元" ); } }
优缺点 优点
缺点
使用场景
当不能采用继承的方式对系统进行扩充或者采用继承不利于系统扩展和维护时。
不能采用继承的情况主要有两类:
第一类是系统中存在大量独立的扩展,为支持每一种组合将产生大量的子类,使得子类数目呈爆炸性增长;
第二类是因为类定义不能继承(如final类)
在不影响其他对象的情况下,以动态、透明的方式给单个对象添加职责。
当对象的功能要求可以动态地添加,也可以再动态地撤销时。
代理和装饰者的区别 静态代理和装饰者模式的区别:
相同点:
都要实现与目标类相同的业务接口
在两个类中都要声明目标对象
都可以在不修改目标类的前提下增强目标方法
不同点:
目的不同 装饰者是为了增强目标对象 静态代理是为了保护和隐藏目标对象
获取目标对象构建的地方不同 装饰者是由外界传递进来,可以通过构造方法传递 静态代理是在代理类内部创建,以此来隐藏目标对象
桥接模式 概念 将抽象与实现分离,使它们可以独立变化。它是用组合关系代替继承关系来实现,从而降低了抽象和实现这两个可变维度的耦合度。
结构 桥接(Bridge)模式包含以下主要角色:
抽象化(Abstraction)角色 :定义抽象类,并包含一个对实现化对象的引用。
扩展抽象化(Refined Abstraction)角色 :是抽象化角色的子类,实现父类中的业务方法,并通过组合关系调用实现化角色中的业务方法。
实现化(Implementor)角色 :定义实现化角色的接口,供扩展抽象化角色调用。
具体实现化(Concrete Implementor)角色 :给出实现化角色接口的具体实现。
实现 开发一个跨平台视频播放器,可以在不同操作系统平台(如Windows、Mac、Linux等)上播放多种格式的视频文件,常见的视频格式包括RMVB、AVI、WMV等。该播放器包含了两个维度,适合使用桥接模式。
抽象化角色
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 public abstract class OperatingSystem { protected VideoFile videoFile; public OperatingSystem (VideoFile videoFile) { this .videoFile = videoFile; } public abstract void play (String fileName) ; }
扩展抽象化角色
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 public class Windows extends OperatingSystem { public Windows (VideoFile videoFile) { super (videoFile); } @Override public void play (String fileName) { videoFile.decode(fileName); } }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 public class Mac extends OperatingSystem { public Mac (VideoFile videoFile) { super (videoFile); } @Override public void play (String fileName) { videoFile.decode(fileName); } }
实现化角色
1 2 3 4 5 6 7 8 9 10 public interface VideoFile { void decode (String fileName) ; }
具体实现化角色
1 2 3 4 5 6 7 8 9 10 public class RmvbFile implements VideoFile { @Override public void decode (String fileName) { System.out.println("rmvb视频文件:" + fileName); } }
1 2 3 4 5 6 7 8 9 10 public class AviFile implements VideoFile { @Override public void decode (String fileName) { System.out.println("avi视频文件:" + fileName); } }
测试
1 2 3 4 5 6 7 8 9 10 public class App { public static void main (String[] args) { OperatingSystem system = new Mac (new AviFile ()); system.play("黑豹2" ); } }
特点
使用场景
当一个类存在两个独立变化的维度,且这两个维度都需要进行扩展时。
当一个系统不希望使用继承或因为多层次继承导致系统类的个数急剧增加时。
当一个系统需要在构件的抽象化角色和具体化角色之间增加更多的灵活性时。避免在两个层次之间建立静态的继承联系,通过桥接模式可以使它们在抽象层建立一个关联关系。
外观模式 概念 又名门面模式,是一种通过为多个复杂的子系统提供一个一致的接口,而使这些子系统更加容易被访问的模式。该模式对外有一个统一接口,外部应用程序不用关心内部子系统的具体的细节,这样会大大降低应用程序的复杂度,提高了程序的可维护性。
结构 外观(Facade)模式包含以下主要角色:
外观(Facade)角色:为多个子系统对外提供一个共同的接口。
子系统(Sub System)角色:实现系统的部分功能,客户可以通过外观角色访问它。
实现 智能家电控制:晚上每次都需要打开灯、打开电视、打开空调;睡觉时关闭灯、关闭电视、关闭空调;操作起来都比较麻烦。所以可以实现一个智能家电控制系统,通过语音直接控制这些智能家电的开启和关闭。类图如下:
外观角色
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 public class SmartApplicationFacade { private Light light; private TV tv; private AirCondition airCondition; public SmartApplicationFacade () { light = new Light (); tv = new TV (); airCondition = new AirCondition (); } public void say (String message) { if (message.contains("打开" )){ on(); }else if (message.contains("关闭" )){ off(); }else { System.out.println("不太清楚要做什么" ); } } public void on () { light.on(); tv.on(); airCondition.on(); } public void off () { light.off(); tv.off(); airCondition.off(); } }
子系统角色
1 2 3 4 5 6 7 8 9 10 11 12 13 public class Light { public void on () { System.out.println("打开电灯" ); } public void off () { System.out.println("关闭电灯" ); } }
1 2 3 4 5 6 7 8 9 10 11 12 public class TV { public void on () { System.out.println("打开电视" ); } public void off () { System.out.println("关闭电视" ); } }
1 2 3 4 5 6 7 8 9 10 11 12 public class AirCondition { public void on () { System.out.println("打开空调" ); } public void off () { System.out.println("关闭空调" ); } }
测试
1 2 3 4 5 6 7 8 9 10 public class App { public static void main (String[] args) { SmartApplicationFacade facade = new SmartApplicationFacade (); facade.say("打开家电" ); facade.say("关闭家电" ); } }
优缺点 优点
降低了子系统与客户端之间的耦合度,使得子系统的变化不会影响调用它的客户类。
对客户屏蔽了子系统组件,减少了客户处理的对象数目,并使得子系统使用起来更加容易。
缺点
使用场景
对分层结构系统构建时,使用外观模式定义子系统中每层的入口点可以简化子系统之间的依赖关系。
当一个复杂系统的子系统很多时,外观模式可以为系统设计一个简单的接口供外界访问。
当客户端与多个子系统之间存在很大的联系时,引入外观模式可将它们分离,从而提高子系统的独立性和可移植性。
组合模式 概念 又名部分整体模式,是用于把一组相似的对象当作一个单一的对象。组合模式依据树形结构来组合对象,用来表示部分以及整体层次。这种类型的设计模式属于结构型模式,它创建了对象组的树形结构。
结构 组合模式主要包含三种角色:
抽象根节点(Component):定义系统各层次对象的共有方法和属性,可以预先定义一些默认行为和属性。
树枝节点(Composite):定义树枝节点的行为,存储子节点,组合树枝节点和叶子节点形成一个树形结构。
叶子节点(Leaf):叶子节点对象,其下再无分支,是系统层次遍历的最小单位。
分类 在使用组合模式时,根据抽象构件类的定义形式,我们可将组合模式分为透明组合模式和安全组合模式两种形式。
透明组合模式
透明组合模式中,抽象根节点角色中声明了所有用于管理成员对象的方法,比如在示例中 MenuComponent
声明了 add
、remove
、getChild
方法,这样做的好处是确保所有的构件类都有相同的接口。透明组合模式也是组合模式的标准形式。
透明组合模式的缺点是不够安全,因为叶子对象和容器对象在本质上是有区别的,叶子对象不可能有下一个层次的对象,即不可能包含成员对象,因此为其提供 add()、remove() 等方法是没有意义的,这在编译阶段不会出错,但在运行阶段如果调用这些方法可能会出错(如果没有提供相应的错误处理代码)
安全组合模式
在安全组合模式中,在抽象构件角色中没有声明任何用于管理成员对象的方法,而是在树枝节点 Menu
类中声明并实现这些方法。安全组合模式的缺点是不够透明,因为叶子构件和容器构件具有不同的方法,且容器构件中那些用于管理成员对象的方法没有在抽象构件类中定义,因此客户端不能完全针对抽象编程,必须有区别地对待叶子构件和容器构件。
实现 打印软件菜单,打印出其包含的所有菜单以及菜单项的名称。
抽象根节点
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 public abstract class MenuComponent { protected String name; protected int level; public void add (MenuComponent menuComponent) { throw new UnsupportedOperationException (); } public void remove (MenuComponent menuComponent) { throw new UnsupportedOperationException (); } public MenuComponent getChild (int index) { throw new UnsupportedOperationException (); } public String getName () { return name; } public abstract void print () ; }
树枝节点
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 import java.util.ArrayList;import java.util.List;public class Menu extends MenuComponent { private List<MenuComponent> menuComponentList = new ArrayList <>(); public Menu (String name,int level) { this .name = name; this .level = level; } @Override public void add (MenuComponent menuComponent) { menuComponentList.add(menuComponent); } @Override public void remove (MenuComponent menuComponent) { menuComponentList.remove(menuComponent); } @Override public MenuComponent getChild (int index) { return menuComponentList.get(index); } @Override public void print () { StringBuilder word = new StringBuilder (); for (int i = 0 ; i < level-1 ; i++) { word.append("-" ); } System.out.println(word+name); for (MenuComponent menuComponent : menuComponentList) { menuComponent.print(); } } }
叶子节点
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 public class MenuItem extends MenuComponent { public MenuItem (String name, int level) { this .name = name; this .level = level; } @Override public void print () { StringBuilder word = new StringBuilder (); for (int i = 0 ; i < level-1 ; i++) { word.append("-" ); } System.out.println(word+name); } }
测试
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 public class App { public static void main (String[] args) { MenuComponent menu1 = new Menu ("菜单管理" ,2 ); menu1.add(new MenuItem ("页面访问" ,3 )); menu1.add(new MenuItem ("展开菜单" ,3 )); menu1.add(new MenuItem ("删除菜单" ,3 )); MenuComponent menu2 = new Menu ("权限管理" ,2 ); menu2.add(new MenuItem ("页面访问" ,3 )); menu2.add(new MenuItem ("提交保存" ,3 )); MenuComponent menu3 = new Menu ("角色管理" ,2 ); menu3.add(new MenuItem ("删除菜单" ,3 )); menu3.add(new MenuItem ("新增角色" ,3 )); Menu menu = new Menu ("系统管理" ,1 ); menu.add(menu1); menu.add(menu2); menu.add(menu3); menu.print(); } }
特点
组合模式可以清楚地定义分层次的复杂对象,表示对象的全部或部分层次,它让客户端忽略了层次的差异,方便对整个层次结构进行控制。
客户端可以一致地使用一个组合结构或其中单个对象,不必关心处理的是单个对象还是整个组合结构,简化了客户端代码。
在组合模式中增加新的树枝节点和叶子节点都很方便,无须对现有类库进行任何修改,符合“开闭原则”。
组合模式为树形结构的面向对象实现提供了一种灵活的解决方案,通过叶子节点和树枝节点的递归组合,可以形成复杂的树形结构,但对树形结构的控制却非常简单。
使用场景 组合模式正是应树形结构而生,所以组合模式的使用场景就是出现树形结构的地方。比如:文件目录显示,多级目录呈现等树形结构数据的操作。
享元模式
努力写作中