本文共 5204 字,大约阅读时间需要 17 分钟。
适配器模式
1、概述 2、适配器模式的用途 3、模式中的角色 4、实现方式 5、类适配器和对象适配器的权衡 6、缺省适配模式
1、概述
适配器模式把一个类的接口变换成客户端所期待的另一种接口,从而使原本因接口不匹配而无法在一起工作的两个类能够在一起工作。
2、适配器模式的用途
即Adapter模式使得原本由于接口不兼容而不能一起工作的那些类可以在一起工作。 下面是两个典型例子3、模式中的角色
3.1 目标接口(Target):客户所期待的接口。目标可以是具体的或抽象的类,也可以是接口。
3.2 需要适配的类(Adaptee):需要适配的类或适配者类。 3.3 适配器(Adapter):通过包装一个需要适配的对象,把原接口转换成目标接口。4、实现方式
(1)类的适配器模式(采用继承实现)
源代码:
//目标接口,或称为标准接口 public interface Target { //普通功能 public void request();}//已存在的、具有特殊功能、但不符合我们既有的标准接口的类 public class Adaptee { public void specificRequest() { System.out.println("被适配类...我是两孔插座 具有特殊功能"); }}//适配器类,继承了被适配类,同时实现标准接口public class Adapter extends Adaptee implements Target { @Override public void request() { System.out.println("我是适配器类 我能适配任何两孔插座 让它正常工作"); this.specificRequest(); } }public class Client { public static void main(String[] args) { Target adapter = new Adapter(); //通过适配器调用特殊功能 adapter.request(); }}
结果:
我是适配器类 我能适配任何两孔插座 让它正常工作 被适配类…我是两孔插座 具有特殊功能(2)对象适配器(采用对象组合方式实现)
源代码://目标接口,或称为标准接口 public interface Target { //普通功能 public void request();}//已存在的、具有特殊功能、但不符合我们既有的标准接口的类 public class Adaptee { public void specificRequest() { System.out.println("被适配类...我是两孔插座 具有特殊功能"); }} //适配器类,直接关联被适配类,同时实现标准接口 class Adapter implements Target { // 直接关联被适配类 private Adaptee adaptee; // 可以通过构造函数传入具体需要适配的被适配类对象 public Adapter(Adaptee adaptee) { this.adaptee = adaptee; } public void request() { // 这里是使用委托的方式完成特殊功能 System.out.println("我是适配器类 我能适配任何两孔插座 让它正常工作"); this.adaptee.specificRequest(); }}public class Client { public static void main(String[] args) { //使用特殊功能类,即适配类, // 需要先创建一个被适配类的对象作为参数 Target adapter = new Adapter(new Adaptee()); adapter.request(); }}
结果:
我是适配器类 我能适配任何两孔插座 让它正常工作 被适配类…我是两孔插座 具有特殊功能从类图中我们也知道需要修改的只不过就是 Adapter 类的内部结构,即 Adapter 自身必须先拥有一个被适配类的对象,再把具体的特殊功能委托给这个对象来实现。使用对象适配器模式,可以使得 Adapter 类(适配类)根据传入的 Adaptee 对象达到适配多个不同被适配类的功能,当然,此时我们可以为多个被适配类提取出一个接口或抽象类。这样看起来的话,似乎对象适配器模式更加灵活一点。
5、类适配器和对象适配器的权衡(摘自大牛博客)
● 类适配器使用对象继承的方式,是静态的定义方式;而对象适配器使用对象组合的方式,是动态组合的方式。
对于对象适配器,一个适配器可以把多种不同的源适配到同一个目标。换言之,同一个适配器可以把源类和它的子类都适配到目标接口。因为对象适配器采用的是对象组合的关系,只要对象类型正确,是不是子类都无所谓。
● 对于对象适配器,要重定义Adaptee的行为比较困难,这种情况下,需要定义Adaptee的子类来实现重定义,然后让适配器组合子类。虽然重定义Adaptee的行为比较困难,但是想要增加一些新的行为则方便的很,而且新增加的行为可同时适用于所有的源。
● 对于类适配器,仅仅引入了一个对象,并不需要额外的引用来间接得到Adaptee。 ● 对于对象适配器,需要额外的引用来间接得到Adaptee。 建议尽量使用对象适配器的实现方式,多用合成/聚合、少用继承。当然,具体问题具体分析,根据需要来选用实现方式,最适合的才是最好的。适配器模式的优点
更好的复用性
系统需要使用现有的类,而此类的接口不符合系统的需要。那么通过适配器模式就可以让这些功能得到更好的复用。 更好的扩展性 在实现适配器功能的时候,可以调用自己开发的功能,从而自然地扩展系统的功能。 适配器模式的缺点过多的使用适配器,会让系统非常零乱,不易整体进行把握。比如,明明看到调用的是A接口,其实内部被适配成了B接口的实现,一个系统如果太多出现这种情况,无异于一场灾难。因此如果不是很有必要,可以不使用适配器,而是直接对系统进行重构。
6、缺省适配模式
缺省适配(Default Adapter)模式为一个接口提供缺省实现,这样子类型可以从这个缺省实现进行扩展,而不必从原有接口进行扩展。作为适配器模式的一个特例,缺省是适配模式在JAVA语言中有着特殊的应用。
就是一个抽象类对功能接口的所有功能做空实现 然后子类继承这个抽象类 这样就可以对部分功能进行实现或拓展了
源代码
功能接口:
/** * 功能接口 谈恋爱后具备如下功能 * 牵手 * 接吻 * 看电影 * 情侣晚餐 * 回家见父母 * @author Administrator * */public interface Love { //牵手 该功能是恋爱后经常会有的波 public void hand(); //接吻 public void kiss(); //看电影 public void lookMovie(); //情侣晚餐 public void have(); //回家见父母 public void backHome(); //女朋友是谁 public String getGirlFriend();}
缺省适配类
/** * 抽象类 * 交往类 实现恋爱接口 * 哈哈 交往多了自然就谈爱了吗 嘻嘻嘻! * 但是交往毕竟不是谈恋爱 虽然它具有了谈恋爱的雏形 但是毕竟不是 * 所以对谈恋爱的所有功能做了空实现 只有架子 * 相当于没有 但是未来可能会有 嘎嘎嘎 * @author Administrator * */public abstract class Communication implements Love { //全是空实现 @Override public void hand() {} @Override public void kiss() {} @Override public void lookMovie() {} @Override public void have() {} @Override public void backHome() {} @Override public String getGirlFriend() { return null; }}
具体实现
/** * 具体的类 汤高 继承了交往Communication抽象类 * 本类汤高已经从交往慢慢恋爱了 所以它已经具备了谈恋爱接口 * 的所有功能 但是了 有些功能他每天不可能天天都调用 * 比如 看电影 情侣晚餐 回家见父母等等 * (因为分隔两地,不能天天见面 但是了 他们一见面就和过年一样高兴 * 他们就可以调用恋爱中的一些功能 * 比如他们 可以看电影 共进晚餐 * ) * * @author Administrator * */public class 汤高 extends Communication { //看电影 public void lookMovie(){ System.out.println("我们一起看电影 哈哈"); } //情侣晚餐 public void have(){ System.out.println("我们一起吃晚餐 哈哈"); } //女朋友是谁 public String getGirlFriend(){ return "周思远"; }}
客户端:
public class Client { public static void main(String[] args) { Love tanggao=new 汤高(); tanggao.lookMovie(); tanggao.have(); System.out.println("女朋友是 :"+tanggao.getGirlFriend()); }}
结果:
我们一起看电影 哈哈 我们一起吃晚餐 哈哈 女朋友是 :周思远在很多情况下,必须让一个具体类实现某一个接口,但是这个类又用不到接口所规定的所有的方法。通常的处理方法是,这个具体类要实现所有的方法,那些有用的方法要有实现,那些没有用的方法也要有空的、平庸的实现。
这些空的方法是一种浪费,有时也是一种混乱。除非看过这些空方法的代码,程序员可能会以为这些方法不是空的。即便他知道其中有一些方法是空的,也不一定知道哪些方法是空的,哪些方法不是空的,除非看过这些方法的源代码或是文档。
缺省适配模式可以很好的处理这一情况。可以设计一个抽象的适配器类实现接口,此抽象类要给接口所要求的每一种方法都提供一个空的方法。就像具体帮助类汤高一样 他只实现某一部分功能 因为他只用到了某一部分功能而已,如果以后要用,再实现其他功能就是,但是今天他不用,就没必要该功能了!
适配器模式的用意是要改变源的接口,以便于目标接口相容。缺省适配的用意稍有不同,它是为了方便建立一个不平庸的适配器类而提供的一种平庸实现。
在任何时候,如果不准备实现一个接口的所有方法时,就可以使用“缺省适配模式”制造一个抽象类,给出所有方法的平庸的具体实现。这样,从这个抽象类再继承下去的子类就不必实现所有的方法了
以上内容来自平时所看书籍和网络资源整理测试所得,如有不完善之处,欢迎指正!