设计模式-结构类模式
代理模式
- 四种不同的代理模式:分别实现了某一个类某一个方法的代理,某一些类(相同接口)某一个方法的代理、某一些类(相同接口)中所有接口方法的代理、所有类中所有非final方法的代理。
https://www.cnblogs.com/cenyu/p/6289209.html
提供了对目标对象另外的访问方式,即通过代理对象访问目标对象.这样做的好处是:可以在目标对象实现的基础上,增强额外的功能操作,即扩展目标对象的功能.
最简单的代理模式:代理类的构造方法里new一个代理类作为成员变量,然后重写代理方法。优点简洁,缺点没有可扩展性
public class Human {public void sleep(){System.out.println("i am sleeping");}}public class HumanProxy {private Human man=null;public HumanProxy(){man=new Human();}public void sleep(){System.out.println("i am going to sleep");man.sleep();System.out.println("i am awake");}}public class Test {public static void main(String[] args) {HumanProxy proxy=new HumanProxy();proxy.sleep();}}静态代理模式:实现满足某接口的所有实现类的代理
实现方法:
- 设定一个接口,针对该接口设计一个代理类,来代理所有该接口的实现类
- 把接口类作为成员变量,在创建代理接口类时初始化
- 重写接口类的代理方法
缺点:因为代理对象需要与目标对象实现一样的接口,所以会有很多代理类,类太多.同时,一旦接口增加方法,目标对象与代理对象都要维护.
下面的例子:AnimalProxy可以代理所有Animal的实现类的sleep方法。根据注入实现类的不同,实现主体方法的不同
动态代理模式:利用JDK反射的原理,对委托类的接口所有方法都进行一次性但是相同预处理,缺点在于不灵活
静态代理可以实现不同的实现方法都要一个个的实现,但是每个预处理可以不一样,也可以一样。如果接口添加新方法的话,则静态代理类要添加新的方法来支持代理。
动态代理可以实现通用的所有接口方法做出相同的预处理,这样,接口类添加新方法,只要预处理的逻辑没变,则代理类的代码不变。public class ProxyFactory {//维护一个目标对象private Object target;public ProxyFactory(Object target){this.target=target;}public Object getProxyInstance(){return Proxy.newProxyInstance(target.getClass().getClassLoader(),target.getClass().getInterfaces(),new InvocationHandler() {@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {System.out.println("开始事务2");//执行目标对象方法Object returnValue = method.invoke(target, args);System.out.println("提交事务2");return returnValue;}});}}//测试类public static void main(String[] args) {Dog target=new Dog();Animal proxy= (Animal) new ProxyFactory(target).getProxyInstance();proxy.sleep();proxy.bark();}cglib代理模式:运行期,通过在内存中创建一个工具类Enhancer继承委托类,在对工具类进行预处理,所有的方法都嵌入了代理逻辑,所有满足这种预处理方法的类的方法都可以被代理(final)的方法除外
适配器模式
原理:不兼容的接口之间的桥梁。它让一个类经过适配之后可以实现不兼容接口的功能。适配器不是在详细设计时添加的,而是解决正在服役的项目的问题
思路分析:
MP3播放器只能放MP3类型的歌曲,要让他支持可以播放MP4的话,可以让MP3类继承MP4类,或者把MP4类用成员变量的方式注入到MP3类里面。
MP3继承MP4类的缺点:
- JAVA单继承,如果继承了MP4类,就不能根据自己业务继承其它类,不能继承
- 如果要扩展其它的兼容功能,例如FLV格式的文件,继承就不行,不能同时继承FLV和MP4
所以最佳是通过注入的形式,实现支持兼容性功能。同理,如果直接注入MP4类的话,那么如果业务变更,还要支持FLV格式的文件播放的话,则还要注入FLV类,我们希望把这个过程剥离开来。于是有了Adapter,他根据业务需要实例化成业务需要的类,可以是FLV,也可以是MP4,甚至可以同时是FLV和MP4,这样只要注入一个Adapter,就可以灵活应对业务变更了。
Adapter就是未来需求变更的改动类了,Adapter要解决的问题是:提供一个方法,通过参数不同,去调用不同兼容类去实现功能。结构如下
上面就是一种很好的模式,但是如果我只要放MP4的歌、或者只要放FLV的歌的话,我都会实例化2个播放器,这样有些浪费内存,如果业务上要求有时放MP4有时放FLV的话,上面就是最好的选择,如果业务一次只需要实现一种接口即可的话,可以按照下面来操作
目标类AudioPlayer,注入了mediaAdapter接口,并且根据需求,实例化
客户端现在可以通过适配之后的AudioPlayer,播放不同类型的歌曲了