简介
装饰模式、代理模式、适配器模式个人感觉非常相似,三者的类图也差不多,不好理解。
区别:
装饰模式:原有功能不能满足现有需求,按照开闭原则的思路,我们通过扩展接口来增强功能。可以装饰多层
代理模式:通过代理对象来控制访问另一个对象(被代理对象)的方法,不对该方法进行直接操作。
适配器模式:解决接口不匹配的问题。
下面通过简单的例子具体说明。
(1)装饰模式
场景描述如下:现有接口Car,拥有方法run,其实现类为BMWCar。
public interface Car { public void run(); }
public class BMWCar implements Car { @Override public void run() { System.out.println("BMW running!"); } }
测试场景如下
public class Client { public static void main(String[] args) { Car car = new BMWCar(); car.run(); } }
输出:“BMW running!”
现在需求变了,要求BMWCar在run之前要说一句“Heihei, I am BMW!”(增强功能)。通常情况不建议改变现有的代码,对修改关闭对扩展开放(开闭原则),可行的方案是扩展一个装饰者DecorateCar,DecorateCar也实现了Car接口,并持有Car的引用,然后通过构造函数实例化。
public class DecorateCar implements Car { private Car car; public DecorateCar(Car car){ this.car = car; } @Override public void run() { System.out.println("Hiahia, I am BMW!"); car.run(); } }
测试场景如下:
public class Client { public static void main(String[] args) { Car car = new DecorateCar(new BMWCar()); car.run(); } }
输出为:
Hiahia, I am BMW!
BMW running!
(2)代理模式
场景:超级明星都有经纪人,若想邀请该明星演出必须通过经纪人,然后经纪人根据明星的日程表来安排演出,若日程表为满,则拒绝本次邀请。
超级明星可以用SuperStar接口表示,具体的明星类如歌手用Singer实现SuperStar。经纪人就是代理,用StarProxy表示。
public interface SuperStar { public void perform(); }
public class Singer implements SuperStar { @Override public void perform() { System.out.println("singing a song!"); } }
public class StarProxy implements SuperStar { private SuperStar superStar; private boolean busyState; public StarProxy(boolean busyState){ superStar = new Singer(); this.busyState = busyState; } @Override public void perform() { if (busyState){ superStar.perform(); }else{ System.out.println("行程已满,无法演出"); } } }
这里通过代理对象控制访问,当busyState为true时,经纪人(代理)安排明星(被代理)演出,否则不能演出。测试场景如下
public class Client { public static void main(String[] args) { SuperStar superStar = new StarProxy(true); superStar.perform(); superStar = new StarProxy(false); superStar.perform(); } }
输出:
singing a song!
行程已满,无法演出
咋一看,真感觉代理模式和装饰模式没啥区别。但是,请注意两个模式的使用场景不一样,装饰模式目的是添加新的功能,可以装饰很多层,如Java IO就是典型的应用。代理模式一般只代理一次,一般用作控制访问。其实也不用纠结设计模式的名称,我们知道这一类问题如何解决不就ok了。
(3)适配器模式
场景:众所周知安卓手机使用安卓充电器(typeC接头),苹果手机使用苹果充电器(iphone接头)。现在你有一台安卓手机和一个苹果充电器,怎么给手机充电。
答案:使用转接头(适配器)将iphone接头转为typeC接头,就可以用苹果充电器给安卓手机充电。
安卓充电器通过typeC给手机充电。
public interface AndroidCharger { public void typeC(); }
public class AndroidChargerImpl implements AndroidCharger { @Override public void typeC() { System.out.println("我能给手机充电"); } }
苹果充电器通过iphone给手机充电。
public interface IphoneCharger { public void iphone(); }
public class IphoneChargerImpl implements IphoneCharger { @Override public void iphone() { System.out.println("我能给手机充电"); } }
适配器就是拿苹果充电器当作安卓充电器使用,这样我们就可以通过info来充电。
public class IphoneAdapter implements AndroidCharger { private IphoneCharger iphoneCharger; public IphoneAdapter(IphoneCharger iphoneCharger){ this.iphoneCharger = iphoneCharger; } @Override public void typeC() { iphoneCharger.iphone(); } }
测试场景如下
public class Client { public static void main(String[] args) { IphoneCharger iphoneCharger = new IphoneChargerImpl(); AndroidCharger androidCharger = new IphoneAdapter(iphoneCharger); androidCharger.typeC(); } }
有一个苹果充电器,然后将其适配成安卓充电器,通过typeC来充电。输出如下:
我能给手机充电
------------------------完-------------------------