**
设计模式(五)之适配器模式
**
- 案例说明
适配器模式很经典的一个例子就是充电器转接头的代码实现,而本文模拟现实业务中当我们对一个固有接口的返回值不满意时,我们在中间加上一个加工返回值的适配器,经转换后达到满足我们需求的例子来体现适配器模式的写法和优缺点。
适配器模式一般有几个角色:被适配者(资源类、源对象)、适配器 、目标对象 、使用者。适配器模式按照适配器整合被适配者的方式,一般分为类适配器模式、对象适配器模式、缺省(接口)适配器模式。下面结合UML类图和代码一起说明。
- 类适配器模式
Source是被适配者,这里可以看作是我们实际业务中调用这个接口中的getSource方法就能得到一个字符串返回值,但我们对这个字符串不太满意,就通过一个适配器Adaptor对这个返回的字符串进行加工后得到Target对象,也就是我们所期望的返回值,再调用Target的getTargetSource方法返回给使用者。
被适配者Source
public class Source {
public String getSource(){
return "字符串类型返回值";
}
}
目标对象
public interface Target {
public String getTargetSource();
}
适配器Adaptor继承了被适配者实现了目标对象
public class Adaptor extends Source implements Target{
@Override
public String getTargetSource() {
return "经过类适配器加工的" + super.getSource();
}
}
我们只需要面向适配器来调用适配器中已经实现的目标方法,就能得到我们实际需要的返回值。
public class Client {
public static void main(String[] args) {
Adaptor adaptor = null;
// 通过适配器来调用被适配者的方法,由适配器内部进行转换
adaptor = new Adaptor();
String target = adaptor.getTargetSource();
System.out.println(target);
}
}
D:\jdk8\bin\java.exe "-javaagent:C:\Program Files\JetBrains\IntelliJ IDEA 2019.2\lib\idea_rt.jar=58132:C:\Program Files\JetBrains\IntelliJ IDEA 2019.2\bin" -Dfile.encoding=UTF-8 -classpath D:\jdk8\jre\lib\charsets.jar;D:\jdk8\jre\lib\deploy.jar;D:\jdk8\jre\lib\ext\access-bridge-64.jar;D:\jdk8\jre\lib\ext\cldrdata.jar;D:\jdk8\jre\lib\ext\dnsns.jar;D:\jdk8\jre\lib\ext\jaccess.jar;D:\jdk8\jre\lib\ext\jfxrt.jar;D:\jdk8\jre\lib\ext\localedata.jar;D:\jdk8\jre\lib\ext\nashorn.jar;D:\jdk8\jre\lib\ext\sunec.jar;D:\jdk8\jre\lib\ext\sunjce_provider.jar;D:\jdk8\jre\lib\ext\sunmscapi.jar;D:\jdk8\jre\lib\ext\sunpkcs11.jar;D:\jdk8\jre\lib\ext\zipfs.jar;D:\jdk8\jre\lib\javaws.jar;D:\jdk8\jre\lib\jce.jar;D:\jdk8\jre\lib\jfr.jar;D:\jdk8\jre\lib\jfxswt.jar;D:\jdk8\jre\lib\jsse.jar;D:\jdk8\jre\lib\management-agent.jar;D:\jdk8\jre\lib\plugin.jar;D:\jdk8\jre\lib\resources.jar;D:\jdk8\jre\lib\rt.jar;D:\ideaworkspace\design_pattern\design\target\classes;D:\dev_tools\repository\org\projectlombok\lombok\1.16.10\lombok-1.16.10.jar com.wd.adaptor.classadaptor.Client
经过类适配器加工的字符串类型返回值
Process finished with exit code 0
- 对象适配器模式
对象适配器模式与类适配器模式的区别就是Adaptor在类适配器模式中是实现了被适配者Source,而在对象适配器模式中是将被适配者Source组合到Adaptor中,其他的都不变。
被适配者
public class Source {
public String getSource(){
return "字符串类型返回值";
}
}
目标对象
public interface Target {
public String getTargetSource();
}
适配器
public class Adaptor implements Target {
private Source source;
public Adaptor(Source source){
this.source = source;
}
@Override
public String getTargetSource() {
return "经过对象适配器加工的" + source.getSource();
}
}
客户端
public class Client {
public static void main(String[] args) {
Adaptor adaptor = null;
Source source = null;
source = new Source();
adaptor = new Adaptor(source);
// 通过适配器来调用被适配者的方法,由适配器内部进行转换
String target = adaptor.getTargetSource();
System.out.println(target);
}
}
D:\jdk8\bin\java.exe "-javaagent:C:\Program Files\JetBrains\IntelliJ IDEA 2019.2\lib\idea_rt.jar=58361:C:\Program Files\JetBrains\IntelliJ IDEA 2019.2\bin" -Dfile.encoding=UTF-8 -classpath D:\jdk8\jre\lib\charsets.jar;D:\jdk8\jre\lib\deploy.jar;D:\jdk8\jre\lib\ext\access-bridge-64.jar;D:\jdk8\jre\lib\ext\cldrdata.jar;D:\jdk8\jre\lib\ext\dnsns.jar;D:\jdk8\jre\lib\ext\jaccess.jar;D:\jdk8\jre\lib\ext\jfxrt.jar;D:\jdk8\jre\lib\ext\localedata.jar;D:\jdk8\jre\lib\ext\nashorn.jar;D:\jdk8\jre\lib\ext\sunec.jar;D:\jdk8\jre\lib\ext\sunjce_provider.jar;D:\jdk8\jre\lib\ext\sunmscapi.jar;D:\jdk8\jre\lib\ext\sunpkcs11.jar;D:\jdk8\jre\lib\ext\zipfs.jar;D:\jdk8\jre\lib\javaws.jar;D:\jdk8\jre\lib\jce.jar;D:\jdk8\jre\lib\jfr.jar;D:\jdk8\jre\lib\jfxswt.jar;D:\jdk8\jre\lib\jsse.jar;D:\jdk8\jre\lib\management-agent.jar;D:\jdk8\jre\lib\plugin.jar;D:\jdk8\jre\lib\resources.jar;D:\jdk8\jre\lib\rt.jar;D:\ideaworkspace\design_pattern\design\target\classes;D:\dev_tools\repository\org\projectlombok\lombok\1.16.10\lombok-1.16.10.jar com.wd.adaptor.objectadaptor.Client
经过对象适配器加工的字符串类型返回值
Process finished with exit code 0
- 缺省(接口)适配器模式
如果我们只要调一个接口中的一个或者某几个方法,所以我们不希望为了调用一个方法或者某几个方法就要去实现接口中的全部方法,那我们就可以在接口与调用者之间加上一个抽象类的适配器,适配器实现接口的全部方法,我们调用这个抽象的适配器时就可以按照自己的需求来重写特定的方法就可以了。
被适配者
public interface Source {
public String getStringSource();
public int getIntegerSource();
public double getDoubleSource();
}
抽象的适配器类去空实现被适配者的方法
public abstract class Adaptor implements Source {
@Override
public String getStringSource() {
return null;
}
@Override
public double getDoubleSource() {
return 0;
}
@Override
public int getIntegerSource() {
return 0;
}
}
假设我们只需要用getStringSource和getIntegerSource方法,我们就在只要重写这两个方法就行了。
public class Client {
public static void main(String[] args) {
// 我们只需要去重写特定的我们所需要的的方法来使用就可以了
Adaptor adaptor = new Adaptor(){
@Override
public String getStringSource(){
return "经过缺省适配器处理的字符串";
}
@Override
public int getIntegerSource(){
return 1024;
}
};
String stringTarget = adaptor.getStringSource();
int intTarget = adaptor.getIntegerSource();
System.out.println("stringTarget=" + stringTarget + " \nintTarget=" + intTarget);
}
}
D:\jdk8\bin\java.exe "-javaagent:C:\Program Files\JetBrains\IntelliJ IDEA 2019.2\lib\idea_rt.jar=59301:C:\Program Files\JetBrains\IntelliJ IDEA 2019.2\bin" -Dfile.encoding=UTF-8 -classpath D:\jdk8\jre\lib\charsets.jar;D:\jdk8\jre\lib\deploy.jar;D:\jdk8\jre\lib\ext\access-bridge-64.jar;D:\jdk8\jre\lib\ext\cldrdata.jar;D:\jdk8\jre\lib\ext\dnsns.jar;D:\jdk8\jre\lib\ext\jaccess.jar;D:\jdk8\jre\lib\ext\jfxrt.jar;D:\jdk8\jre\lib\ext\localedata.jar;D:\jdk8\jre\lib\ext\nashorn.jar;D:\jdk8\jre\lib\ext\sunec.jar;D:\jdk8\jre\lib\ext\sunjce_provider.jar;D:\jdk8\jre\lib\ext\sunmscapi.jar;D:\jdk8\jre\lib\ext\sunpkcs11.jar;D:\jdk8\jre\lib\ext\zipfs.jar;D:\jdk8\jre\lib\javaws.jar;D:\jdk8\jre\lib\jce.jar;D:\jdk8\jre\lib\jfr.jar;D:\jdk8\jre\lib\jfxswt.jar;D:\jdk8\jre\lib\jsse.jar;D:\jdk8\jre\lib\management-agent.jar;D:\jdk8\jre\lib\plugin.jar;D:\jdk8\jre\lib\resources.jar;D:\jdk8\jre\lib\rt.jar;D:\ideaworkspace\design_pattern\design\target\classes;D:\dev_tools\repository\org\projectlombok\lombok\1.16.10\lombok-1.16.10.jar com.wd.adaptor.interfaceadaptor.Client
stringTarget=经过缺省适配器处理的字符串
intTarget=1024
Process finished with exit code 0
- 总结
系统需要复用现有类,而该类的接口不符合系统的需求,可以使用适配器模式使得原本由于接口不兼容而不能一起工作的那些类可以一起工作;多个组件功能类似,但接口不统一且可能会经常切换时,可使用适配器模式,使得客户端可以以统一的接口使用它们。