今天复习这样一种设计模式,当客户端想要使用一个类,但是这个类的接口并不符合客户端的要求时,我们就会想到这个设计模式——
适配器设计模式,把一个类的接口变换成客户端所期待的另一种接口,从而使原本因接口不匹配而无法在一起工作的两个类能够在
一起工作。
适配器模式分为两种:类的适配器模式和对象的适配器模式。
首先,这两种模式的类图关系分别为:
1.类的适配器模式
2.对象的适配器模式
然后,来个实例理解理解,反正这个设计模式,我理解的时候也是想了挺长时间的,希望大家好好把握“协同”工作的意义。
1.类的适配器模式
源角色:
适配器:
客户端:
如上代码所示,正确的输出应该是这样的:
结果和类适配器模式的示例结果完全一样。 最后,在网上找了点这两种适配器模式的对比,贴上来大家自己看看吧
(1)类适配器使用对象继承的方式,是静态的定义方式;而对象适配器使用对象组合的方式,是动态组合的方式。
(2)对于类适配器,由于适配器直接继承了Adaptee,使得适配器不能和Adaptee的子类一起工作,因为继承是静态的关系,当适配器继承了Adaptee后,就不可能再去处理 Adaptee的子类了。
(3)对于对象适配器,一个适配器可以把多种不同的源适配到同一个目标。换言之,同一个适配器可以把源类和它的子类都适配到目标接口。因为对象适配器采用的是对象组合的关系,只要对象类型正确,是不是子类都无所谓。
(4)对于类适配器,适配器可以重定义Adaptee的部分行为,相当于子类覆盖父类的部分实现方法。
(5)对于对象适配器,要重定义Adaptee的行为比较困难,这种情况下,需要定义Adaptee的子类来实现重定义,然后让适配器组合子类。虽然重定义Adaptee的行为比较困难,但是想要增加一些新的行为则方便的很,而且新增加的行为可同时适用于所有的源。
(6)对于类适配器,仅仅引入了一个对象,并不需要额外的引用来间接得到Adaptee。
(7)对于对象适配器,需要额外的引用来间接得到Adaptee。
适配器模式分为两种:类的适配器模式和对象的适配器模式。
首先,这两种模式的类图关系分别为:
1.类的适配器模式
然后,来个实例理解理解,反正这个设计模式,我理解的时候也是想了挺长时间的,希望大家好好把握“协同”工作的意义。
1.类的适配器模式
目标接口:
package com.xdccl.zwj.AdapterDesignPatterns.ClassAdapterDesignPattern;
//目标角色:类适配器设计模式中,这里只能是一个接口
public interface Target {
public void get110v() ;
public void get220v() ;
}
源角色:
package com.xdccl.zwj.AdapterDesignPatterns.ClassAdapterDesignPattern;
//源角色,只具有特定的方法,如果Target接口改变,这个类是不能被修改的
//同时,这个类的接口和标准接口不同,需要这个类和实现标准接口的类协同操作(理解这里的协同的意思)
public class Adaptee {
public void get220v(){
System.out.println("源角色----220v");
}
}
适配器:
package com.xdccl.zwj.AdapterDesignPatterns.ClassAdapterDesignPattern;
//适配器:对源角色进行扩展,实现了目标角色,从而使得目标角色改动时候,不用改动源角色,只要改动适配器即可
public class Adapter extends Adaptee implements Target {
@Override
public void get110v() {
System.out.println("适配器---110v");
}
}
客户端:
package com.xdccl.zwj.AdapterDesignPatterns.ClassAdapterDesignPattern;
public class Client {
public static void main(String args[]){
//面向客户端的只有Target接口
Target target = new Adapter() ; //在需求中,这里的代码是不可以改变的
target.get110v() ; //这里使用的是适配器中的方法
target.get220v() ; //这里使用的源角色的方法
//从代码中可以看出,都是使用target对象,却可以使用源角色和适配器中的方法,这里就体现了协同的作用
}
}
如上代码所示,正确的输出应该是这样的:
适配器---110v
源角色----220v
这里的目标接口和源角色的代码可以不变,需要对适配器和客户端代码进行修改:
适配器:
package com.xdccl.zwj.AdapterDesignPatterns.ObjectAdapterDesignPattern.copy;
//这里和类适配器不同的就是,需要拿到Adaptee的引用,这样,相当于是一个代理的作用了
public class Adapter implements Target {
private Adaptee adaptee ;
public Adapter(Adaptee adaptee){ //构造函数
this.adaptee = adaptee ;
}
@Override
public void get110v() {
System.out.println("适配器---110v");
}
@Override
public void get220v() {
adaptee.get220v() ;
}
}
客户端:
package com.xdccl.zwj.AdapterDesignPatterns.ObjectAdapterDesignPattern.copy;
public class Client {
public static void main(String args[]){
//面向客户端的只有Target接口
Adaptee adaptee = new Adaptee() ; //这里需要将源对象作为参数传到适配器中
Target target = new Adapter(adaptee) ; //对象适配器的好处就是这里的Target可以是一个类
target.get110v() ; //这里使用的是适配器中的方法
target.get220v() ; //这里使用的源角色的方法
//从代码中可以看出,都是使用target对象,却可以使用源角色和适配器中的方法,这里就体现了协同的作用
}
}
结果和类适配器模式的示例结果完全一样。 最后,在网上找了点这两种适配器模式的对比,贴上来大家自己看看吧
(1)类适配器使用对象继承的方式,是静态的定义方式;而对象适配器使用对象组合的方式,是动态组合的方式。
(2)对于类适配器,由于适配器直接继承了Adaptee,使得适配器不能和Adaptee的子类一起工作,因为继承是静态的关系,当适配器继承了Adaptee后,就不可能再去处理 Adaptee的子类了。
(3)对于对象适配器,一个适配器可以把多种不同的源适配到同一个目标。换言之,同一个适配器可以把源类和它的子类都适配到目标接口。因为对象适配器采用的是对象组合的关系,只要对象类型正确,是不是子类都无所谓。
(4)对于类适配器,适配器可以重定义Adaptee的部分行为,相当于子类覆盖父类的部分实现方法。
(5)对于对象适配器,要重定义Adaptee的行为比较困难,这种情况下,需要定义Adaptee的子类来实现重定义,然后让适配器组合子类。虽然重定义Adaptee的行为比较困难,但是想要增加一些新的行为则方便的很,而且新增加的行为可同时适用于所有的源。
(6)对于类适配器,仅仅引入了一个对象,并不需要额外的引用来间接得到Adaptee。
(7)对于对象适配器,需要额外的引用来间接得到Adaptee。
(8)建议尽量使用对象适配器的实现方式,多用合成/聚合、少用继承。当然,具体问题具体分析,根据需要来选用实现方式,最适合的才是最好的。
看完了这些给大家抛出一个小问题,如果目标接口不是一个接口,而变成了目标类,那么代码应该做什么改变呢?完成这个小问题来检验下有没有很好的理解这个设计模式吧。