一、概念
将一个类的接口转换成客户希望的另外一个接口,适配器模式使得原本由于接口不兼容而不能一起工作的那些类可以一起工作。
二、模式动机
适配器的本质就是转换类型(源接口的功能和目标接口的功能相同或者相近,如此转换才有意义),目的就是复用已有的功能。
三、模式的结构
适配器跟据实现方式可以分为类适配器和对像适配器,简单结构图如下:
(类适配器) (对象适配器)
类适配器:
1.Target:期待得到的目标接口,由于是类适配器,所以这个必须是Interface
2.Adaptee:被适配的接口,是一个具体类
3.Adapter:适配器,具体类
类适配器的实现思路是,通过继承被适配器的类Adaptee,实现目标接口Target。由于继承是静态关系,所以他只能适配当前的类Adaptee,而不能适配Adaptee的其它子类。由于是继承关系,适配器类可以很方便的重写被适配器类中的方法。
类适配器的样例代码如下:
package adapter.classpattern; /** * 期望的目标接口 * @ClassName: Target * @author beteman6988 * @date 2017年11月11日 下午11:20:44 * */ public interface Target { /** * 源类也有的方法 * @Title: sampleOperation1 * @param * @return void * @throws */ public void sampleOperation1(); /** * 源类中没有的方法 * @Title: sampleOperation2 * @param * @return void * @throws */ public void sampleOperation2(); }
package adapter.classpattern; /** * 被适配的类 * @ClassName: Adaptee * @author beteman6988 * @date 2017年11月11日 下午11:23:22 * */ public class Adaptee { public void sampleOperation1() { //Do something...... } }
package adapter.classpattern; public class Adapter extends Adaptee implements Target { /** * 目标接口需要,但是源类中没有的方法,需自已恰当的实现 */ @Override public void sampleOperation2() { // TODO Auto-generated method stub } }
对象适配器:
1.Target:期待得到的目标接口,可以是具体类或者抽象类
2.Adaptee:被适配的接口
3.Adapter:适配器,具体类
对象适配器的实现思路是:适配器类(Adapter)继承或实现目标接口(Target),适配器类含有被适配器接口的对象成员, 适配器类通过委派,调用被适配对象的方法以实现复用,达到实现目标接口到源接口(被适配器接口)的转换。
对象适配器,由于引用的是被适配接口的对象,所以他可以适配被适配接口的所有子类或者实现,但是无法覆盖被适配接口的方法,只能适过一个子类继承适配器接口,改写父类的方法,然后适配器类适配这个子类的方法来实现。
样例代码如下:
package adapter.objpattern; /** * 期望的目标接口 * @ClassName: Target * @author beteman6988 * @date 2017年11月11日 下午11:20:44 * */ public interface Target { /** * 目标接口的方法 * @Title: sampleOperation1 * @param * @return void * @throws */ public void sampleOperation1(); /** * 目标接口的方法 * @Title: sampleOperation2 * @param * @return void * @throws */ public void sampleOperation2(); }
package adapter.objpattern; /** * 被适配的类 * @ClassName: Adaptee * @author beteman6988 * @date 2017年11月11日 下午11:23:22 * */ public class Adaptee { public void sampleOperationA() { System.out.println("适配到-Adaptee.sampleOperationA()"); } }
package adapter.objpattern; public class Adapter extends Adaptee implements Target { private Adaptee adaptee; public Adapter(Adaptee adaptee) { this.adaptee=adaptee; } /** * 目标接口需要的方法 */ @Override public void sampleOperation1() { /** * 通过委派复用被适配对象的方法 */ this.adaptee.sampleOperationA(); } /** * 目标接口需要,但被适配对象不中具备的方法 */ @Override public void sampleOperation2() { //DO Something...... System.out.println("目标接口需要,但被适配对象中不具备的方法,需恰当的实现。。。。。。"); } }
package adapter.objpattern; public class Client { public static void main(String[] args) { Adaptee adaptee=new Adaptee(); //被适配的源对象 Target target=new Adapter(adaptee); //将源对象适配到目标接口的适配器 target.sampleOperation1(); //目标接口调用自已的方法,以期待源对象来实现。 target.sampleOperation2(); } }
运行结果如下:
适配到-Adaptee.sampleOperationA()
目标接口需要,但被适配对象中不具备的方法,需恰当的实现。。。。。。
四、缺省适配器
缺省适配器就是用一个抽像类继承或实现目标接口,对目标接口中的所有方法提供平庸实现,从该抽象类继承的子类,只需要覆写父类中所需要关注的方法。而不需要实现目标接口的所有方法。比如一个按钮事件的接口,所有按钮都支持单击和双击事件,但是现在有一个按钮只需要对单击事件做出响应,这时这个按钮就和目标期待的接口不匹配,如果要匹配目标接口,这个按钮事件对象就必须对双击事件也要做出响应,用缺省适配器方法就能很好解决这个问题。首先为这个按钮事件接口提供一个抽象类,抽象类平庸实现(什么事都不做)对单击和双击事件的响应,然后只支持单击事件响应的按钮继承于这个抽象类即可,然后只需重写抽象类中的单击按钮响应方法即可。
五、模式样例
就以日常电视的电源插座为位,目前只有一个二孔的插座,而电视插头是三插头,如何让电视的三孔插头插到两孔的插座上,使电视能正常工作,就需要一个两空到三空的转换插座,即就是本例中的转换器Adapter,下面以程序的方式说明整个转换过程。
类模式:
两孔到三孔的插座转换器继承于两孔插座,表示TwoToThreeSocketAdapter是特殊的TwoProngedSocket,他具有TwoProngedSocket的两个极(正极和负极)的功能,且多了一个地极,代码如下:
package adapter.classpattern.sample; /** * 两孔插座 * @ClassName: TwoProngedSocket * @author beteman6988 * @date 2017年11月12日 下午9:32:40 * */ public class TwoProngedSocket { /** * 负极插空 * @Title: requestNegativePole * @param * @return void * @throws */ public void requestNegativePole() { System.out.println("两孔插座的负极插空开始工作。。。。。。"); } /** * 正极插空 * @Title: requestPositivePole * @param * @return void * @throws */ public void requestPositivePole() { System.out.println("两孔插座的正极插空。。。。。。"); } }
package adapter.classpattern.sample; /** * 电视机需要的三插孔插座接口 * @ClassName: ThreeProngedSocket * @author beteman6988 * @date 2017年11月12日 下午9:37:58 * */ public interface ThreeProngedSocket { /** * 负极插孔 * @Title: requestNegativePole * @param * @return void * @throws */ public void requestNegativePole(); /** * 正极插孔 * @Title: requestPositivePole * @param * @return void * @throws */ public void requestPositivePole(); /** * 地极插孔 * @Title: requestEarthPole * @param * @return void * @throws */ public void requestEarthPole(); }
package adapter.classpattern.sample; /** * 两孔到三孔的插座转换器,对外是三孔插座(implements ThreeProngedSocket)或者说具备了三孔插座的功能, * 却是一个特殊的两孔插座(extends TwoProngedSocket) * @ClassName: TwoToThreeSocketAdapter * @author beteman6988 * @date 2017年11月12日 下午9:47:01 * */ public class TwoToThreeSocketAdapter extends TwoProngedSocket implements ThreeProngedSocket{ /** * 两孔插座不具备的第三极 地极接地 */ @Override public void requestEarthPole() { System.out.println("地极插空接地开始工作。。。。。。"); } }
package adapter.classpattern.sample; /** * 电视类,具备播放功能 * @ClassName: TV * @author beteman6988 * @date 2017年11月12日 下午10:00:15 * */ public class TV { /** * 电视播放时,需要一个三孔插坐,或者具备三孔插座功能(implements ThreeProngedSocket)的插座 * @Title: play * @param @param socket * @return void * @throws */ public void play(ThreeProngedSocket socket) { socket.requestNegativePole(); //负极能正常工作 socket.requestPositivePole(); //正极能正常工作 socket.requestEarthPole(); //地极能正常工作 System.out.println("电视正在播放。。。。。"); } public static void main(String[] args) { TV tv=new TV(); //目前房间只有一个两孔插座,和一个两孔到三孔的插座转的换器,那么转换器插到两孔插座上,上面的三孔插座即可供电视机用。 ThreeProngedSocket socket=new TwoToThreeSocketAdapter(); //给电视提供一个具有三孔插座功能的转换器 tv.play(socket); } }
运行结果如下:
两孔插座的负极插空开始工作。。。。。。
两孔插座的正极插空。。。。。。
地极插空接地开始工作。。。。。。
电视正在播放。。。。。
对像模式:
三孔插座转换器想具备对外提供电力的功能,必须给他提供一个真实的两孔插座对象,当对他请求正负极时,通过委派给真实的两孔插座的正负极对外提供电力。
package adapter.objpattern.sample; /** * 两孔插座 * @ClassName: TwoProngedSocket * @author beteman6988 * @date 2017年11月12日 下午9:32:40 * */ public class TwoProngedSocket { /** * 负极插孔 * @Title: requestNegativePole * @param * @return void * @throws */ public String requestNegativePole() { return "两孔插座的负极插空开始工作。。。。。。"; } /** * 正极插孔 * @Title: requestPositivePole * @param * @return void * @throws */ public String requestPositivePole() { return "两孔插座的正极插空。。。。。。" ; } }
package adapter.objpattern.sample; /** * 电视机需要的三插空插座接口 * @ClassName: ThreeProngedSocket * @author beteman6988 * @date 2017年11月12日 下午9:37:58 * */ public interface ThreeProngedSocket { /** * 负极插孔 * @Title: requestNegativePole * @param * @return void * @throws */ public void requestNegativePole(); /** * 正极插孔 * @Title: requestPositivePole * @param * @return void * @throws */ public void requestPositivePole(); /** * 地极插孔 * @Title: requestEarthPole * @param * @return void * @throws */ public void requestEarthPole(); }
package adapter.objpattern.sample; /** * 两孔到三孔的插座转换器,对外是三孔插座(implements ThreeProngedSocket)或者说具备了三孔插座的功能, * 但是他如果真实具备三孔插作的功能对外提供电力,则必须给他提供一个具有电力的两孔插作(TwoProngedSocket)对象 * 他的正负极通过委派这个两孔插座对象的正负极对外提供电力,地极则自已实现接地 * @ClassName: TwoToThreeSocketAdapter * @author beteman6988 * @date 2017年11月12日 下午9:47:01 * */ public class TwoToThreeSocketAdapter implements ThreeProngedSocket{ //具有电力功能的两孔插座 private TwoProngedSocket twoProngedSocket; /** * 给三也插座转换器提供具有电力功能的两孔插座 * @param twoProngedSocket */ public TwoToThreeSocketAdapter(TwoProngedSocket twoProngedSocket) { this.twoProngedSocket=twoProngedSocket; } /** * 两孔插座不具备的第三极 地极接地 */ @Override public void requestEarthPole() { System.out.println("地极插空接地开始工作。。。。。。"); } @Override public void requestNegativePole() { System.out.println("委派"+twoProngedSocket.requestNegativePole()); } @Override public void requestPositivePole() { System.out.println("委派"+twoProngedSocket.requestPositivePole()); } }
package adapter.objpattern.sample; /** * 电视类,具备播放功能 * @ClassName: TV * @author beteman6988 * @date 2017年11月12日 下午10:00:15 * */ public class TV { /** * 电视播放时,需要一个三孔插坐,或者具备三孔插座功能(implements ThreeProngedSocket)的插座 * @Title: play * @param @param socket * @return void * @throws */ public void play(ThreeProngedSocket socket) { socket.requestNegativePole(); //负极能正常工作 socket.requestPositivePole(); //正极能正常工作 socket.requestEarthPole(); //地极能正常工作 System.out.println("电视正在播放。。。。。"); } public static void main(String[] args) { TV tv=new TV(); //两孔插座 TwoProngedSocket twoProngedSocket=new TwoProngedSocket(); //给三空插座转换器提供一个两孔插座,让他具备三孔插座的功能 ThreeProngedSocket socket=new TwoToThreeSocketAdapter(twoProngedSocket); //给电视提供一个具有三孔插座功能的转换器 tv.play(socket); } }
运行结果如下:
委派两孔插座的负极插空开始工作。。。。。。
委派两孔插座的正极插空。。。。。。
地极插空接地开始工作。。。。。。
电视正在播放。。。。。
六、模式优缺点
优点:更好的复用性,如果已存的类功能已经有了,只是与目标接口不兼容,通过使用适配器,就可以让已有的功能得到更好的复用。
缺点:过多的使用适配器,会让系统非常的凌乱,不易整体把握。如果可行的话,不如直接引用目标对象。