参考博客:http://www.javashuo.com/article/p-mjravmdr-br.htmlhtml
http://www.javashuo.com/article/p-tbgivpuw-u.htmljava
参考以上博客做出的小总结node
写完下面的内容以后的总结:app
要区分装饰模式和代理模式, 不能只从代码结构上区分, 而更要从两个模式的 使用目的 和 使用方式 区分ide
好比装饰器模式的目的, 是为了加强代码 , 为了让装饰以前的类具备更多的功能. 在使用方式上, 装饰器能够一层一层叠加使用性能
而代理模式 是为了控制对被代理的对象的访问的同时能使用其功能, 使用方式上, 使用者是看不到被代理的对象的测试
一. 装饰模式this
装饰模式没有被装饰类的控制权, 装饰类只是将被装饰类的功能拓展, 两个类都实现同一接口, 也就是装饰类能够装饰实现了这一接口的任何其余类spa
装饰器模式的特色就是, 咱们经常将被装饰类做为参数传递进装饰器中, 如IO流的各类装饰器类.net
这里咱们举个栗子:
(1) 首先实现一个女孩接口, 她有一个特色就是漂亮
public interface Girl { void beauty(); }
(2)而后你的女友实现了这个接口, 并且你女票长得很漂亮
public class GirlFriend implements Girl { @Override public void beauty() { System.out.println("your girlfriend is beautiful"); } }
(3)而后咱们有一个能够打扮女孩的化妆师, 这个化妆师也是个女孩
public class Dresser implements Girl { private Girl customer; //每一个化妆师都有一个顾客 public Dresser(Girl customer){ //顾客进店了 this.customer = customer; } @Override public void beauty() { System.out.println("Dress girl"); customer.beauty(); } }
(4)而后咱们就来看看化妆以前和化妆以后的区别吧
@Test public void WrapperTest(){ Girl girlfriend = new GirlFriend(); girlfriend.beauty(); //化妆以前的女友 Girl girlFriend = new Dresser(girlfriend); //女友被化妆师打扮后 girlFriend.beauty(); }
your girlfriend is beautiful --------------------------- Dress girl your girlfriend is beautiful
(5)让多个化妆师层层叠进化妆
定义另外一个新的化妆师
public class OtherDresser implements Girl { private Girl girl; public OtherDresser(Girl girl){ this.girl = girl; } @Override public void beauty() { System.out.println("Dress your girl too"); girl.beauty(); } }
(6)让这个化妆师再帮你女友画一次妆
Girl girlfriend = new GirlFriend(); girlfriend.beauty(); //分割线 System.out.println("---------------------------"); Girl girlFriend = new Dresser(girlfriend); girlFriend.beauty(); //分割线 System.out.println("---------------------------"); Girl girl = new OtherDresser(girlFriend); girl.beauty();
获得的代码以下:
your girlfriend is beautiful --------------------------- Dress girl your girlfriend is beautiful --------------------------- Dress your girl too Dress girl your girlfriend is beautiful
看出来装饰器的好处了吧? 其实根据对IO流的使用知识, 咱们能够像这样使用装饰器
@Test public void WrapperTest(){ Girl girlfriend = new Dresser(new OtherDresser(new GirlFriend())); girlfriend.beauty(); }
获得的结果以下:
Dress girl
Dress your girl too
your girlfriend is beautiful
只是随便举个例子, 为了更好的理解
装饰器模式要关注的点是
二. 代理模式
代理模式有被代理类的控制权,代理类能够继承被代理类的接口,好比静态代理, 也能够不继承接口,好比动态代理
可是代理模式下, 限制了客户端对被代理类的访问, 也就是客户端不知道被代理的是哪个,
代理模式的特色就是, 咱们经常在代理类中实现被代理类的实例, 而不是将被代理类的实例做为参数传递进 代理类中... 这就是为啥限制了对被代理类的访问
原谅我不知道咋说
代理模式有两种: 静态代理 和 动态代理
动态代理又分为: 基于继承接口类的动态代理 和 基于子类的动态代理
1.静态代理模式:
(1)实现一样的 接口+实现类
public interface Girl { void beauty(); } public class GirlFriend implements Girl { @Override public void beauty() { System.out.println("your girl is beautiful"); } }
(2)创造一个代理类, 这个代理人也是一个女孩子, 可是又与装饰器不一样
public class GirlProxy implements Girl { Girl girl; public GirlProxy(){ girl = new GirlFriend(); //不一样点: 看到没, 女友本身跑到代理人手上了 你本身直接是见不到女友的 } @Override public void beauty() { System.out.println("the proxy said:"); girl.beauty(); } }
(3)来看看,怎么用代理模式
public class ProxyTest { @Test public void test1(){ Girl girlfriend = new GirlProxy(); //你没办法直接见你女友了, 你只能经过代理人见到 girlfriend.beauty(); } }
the proxy said:
your girl is beautiful
这样一看, 与装饰器的区别就来了
2.动态代理模式
静态代理的受限于接口的实现,而动态代理是经过反射,动态地获取对象接口的类型, 从而获取相关特性进行代理, 动态代理可以为全部委托方进行代理.
动态代理模式的经典应用就是Spring中的AOP
2.1 基于继承接口类的动态代理
(1)首先咱们来实现一个动态代理, 就叫GenericProxy吧
public class GenericProxy implements InvocationHandler { private Object proxyTarget; //声明一个代理目标 public Object getProxyInstance(Object target){ this.proxyTarget = target; //经过这个方法,将代理目标和代理关联 //返回一个代理后的实例,这里的参数能够参考AOP文章中的说明 return Proxy.newProxyInstance(proxyTarget.getClass().getClassLoader(),proxyTarget.getClass().getInterfaces(),this); } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println("proxy do something"); Object methodObject = method.invoke(proxyTarget,args); System.out.println("proxy end something"); return methodObject; } }
(2)而后咱们看看动态代理的使用吧
@Test public void test1(){ Girl girlfriend = new GirlFriend(); GenericProxy gp = new GenericProxy(); Girl girlFriend = (Girl) gp.getProxyInstance(girlfriend); girlFriend.beauty(); }
作到这里突然发现, 奇怪, 为何和装饰器同样了呢, 为啥我能够见到我女友了呢?
我查询动态代理和静态代理的区别后, 看到了这个博客, 了解了区别: http://www.javashuo.com/article/p-yjktehxc-et.html
动态代理是为了解决静态代理的缺点: 每个代理类只能为一个接口服务,若是想要转为加强另外一个接口, 则须要一个新的代理类, 这样程序开发中必然会产生许多代理类
好比你只想测试全部接口的实现类的方法的性能, 可是你却要为每一个接口写一个性能测试的代理类
所以,上面的基于接口实现类的动态代理就帮助你解决了这一问题, 由于它能够为任何Object类进行代理
2.2 基于子类的动态代理
在通常状况下, 咱们想加强的类多是没有实现任何接口的普通类, 在这种状况下, 要如何对其进行代理?
如下取自博客:http://www.javashuo.com/article/p-mjravmdr-br.html
假设目前有个项目,A公司写了一个Computer类已经整合到某一个jar包里面,这个类是个普通类没有任何抽象角色(没有实现任何接口)。可是B公司须要对Computer类内的方法进行增强、拓展。那B公司怎么办?目前只有两个方法:一、拿A公司代码反编译,而后修改A公司里面这个类的方法。 二、拿A公司代码反编译,让A公司Computer类进行抽象化,而后B公司使用基于接口的动态代理对方法进行拓展。很明显,这两个方法都是违背了面向对象设计的思想的 (①高内聚低耦合 ②开闭原则[OCP]:对拓展开放,对修改关闭)
在这种状况下基于子类的动态代理产生了。基于子类的动态代理不是由JDK内附功能。须要引入第三方的开发包:CGlib,使用这个子类的代理须要引入cglib-nodep.jar
CGLib在代码上的使用基本和基于接口的动态代理同样,使用方法极为类似,仅表如今CGLib代理时使用的是方法拦截器MethodInterceptor。
(1)建立一个没有实现任何接口的普通类
public class GirlFriend { public void beauty(){ System.out.println("your girlfriend is beautiful"); }
public void love(){
System.out.println("your girlfriend love you");
}
}
(2)实现一个基于子类的动态代理: 记得要有cglib的包
public class GirlProxy implements MethodInterceptor { private Object proxyTarget; public Object getProxyInstance(Object target){ this.proxyTarget = target; return Enhancer.create(target.getClass(),target.getClass().getInterfaces(),this); } @Override public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable { Object methodResult = null; if(method.getName().equals("beauty")){ System.out.println("proxy do something"); methodResult = method.invoke(proxyTarget,args); //启动委托类方法的第一种 System.out.println("proxy do something end"); } else { System.out.println("proxy dont wanna do something"); methodProxy.invokeSuper(proxy,args); //启动委托类方法的第二种 } return methodResult; } }
在这个类里, 咱们实现了和前面相似的方法, 根据委托的方法的不一样, 进行不一样的加强
而在启动委托类方法 的方式 上 又有两种不一样
(3)看看测试结果:
@Test public void test3(){ GirlProxy gp = new GirlProxy(); GirlFriend gf = (GirlFriend)gp.getProxyInstance( new GirlFriend()); gf.beauty(); gf.love(); }
proxy do something your girlfriend is beautiful proxy do something end proxy dont wanna do something your girlfriend love you
能够看到, 根据使用的方法的不一样, 代理人进行了不一样的操做(其实就是根据反射获得的方法名做出不一样操做)
这个的目的是为了说明, 代理类会在你每次调用委托类的方法时,进行一次intercept(拦截), 而后你就能够根据不一样的方法进行不一样的加强
(4)再试试让这个代理来代理其余类:
public class Child { public void eat(){ System.out.println("child eat"); } public void run(){ System.out.println("child run"); } public void breath(){ System.out.println("child breath"); } }
@Test public void test3(){ GirlProxy gp = new GirlProxy(); Child child = (Child) gp.getProxyInstance( new Child()); child.breath(); child.eat(); child.run(); }
proxy dont wanna do something child breath proxy dont wanna do something child eat proxy dont wanna do something child run
能够看到, 代理动了, 它也拦截到了不是GirlFriend的类的方法(由于它对全部Object类有效)
(5)最后咱们来做个死, 用装饰器的方式使用它
既然咱们看到了, 动态代理在使用方式上, 彷佛和装饰器模式有点类似, 那咱们用使用装饰器的方式来使用它, 这里我又实现了另外一个CGLIB动态代理类HealthHandle, 代码参考上面
GirlProxy gp = new GirlProxy(); HealthHandle hh = new HealthHandle(); //另外一个动态代理类 Child child = (Child) hh.getProxyInstance(gp.getProxyInstance( new Child())); child.breath(); child.eat(); child.run();
获得的结果呢?
net.sf.cglib.core.CodeGenerationException: java.lang.reflect.InvocationTargetException-->null
Caused by: java.lang.ClassFormatError: Duplicate method name&signature in class file DesignPattern/ProxyPattern/CGlibPattern/Child$$EnhancerByCGLIB$$edb29237$$EnhancerByCGLIB$$a32ddec8 at java.lang.ClassLoader.defineClass1(Native Method) at java.lang.ClassLoader.defineClass(ClassLoader.java:763) ... 42 more
emmmmmmmm 大概懂什么意思,就是说不出来,总之就是,行不通的意思....
最后是,官方推荐对委托方法进行调用时, 使用MethodProxy,①官方说速度比较快 ②在intercept内调用委托类方法时不用保存委托对象引用
以上内容参考自最上面的博客, 加上部分本身的理解