适配器模式和门面模式是2种使用比较多的结构型设计模式。
适配器模式的作用是让原本不兼容的接口适配成可以一起使用的接口,比如我们生活中的USB转接头。
而门面模式提供一组统一的高层接口,让子系统更容易用。比如,在移动app上请求一个服务,如果需要多个请求来完成,势必造成通信资源
1.适配器模式
假如有下面一个业务,要做一道泡茶的工序,包括洗茶具、洗茶叶、泡茶3步,现在的2段代码分别是用A茶具泡普洱茶和用B茶具泡龙井茶
代码语言:javascript复制public class MakeTeaPuer {
public void makeTeaPuer(){
System.out.println("make tea puer");
}
public void washTeaPuer(){
System.out.println("wash tea puer");
}
public void washCupA(){
System.out.println("wash cup A");
}
}
public class MakeTeaLj {
public void makeTeaLj(){
System.out.println("make tea longjing");
}
public void washTeaLj(){
System.out.println("wash tea longjing");
}
public void washCupB(){
System.out.println("wash cup B");
}
}
如果我们要创建一个抽象的泡茶任务,然后用接口来调用任务,上面的代码就不好处理了。现在我们使用适配器设计模式,引入一个适配接口,如下:
代码语言:javascript复制public interface IMakeTeaAdaptee {
void process1();
void process2();
void process3();
}
代码语言:javascript复制
我们定义冲泡普洱茶的任务类:
代码语言:javascript复制public class PuerAProcessor implements IMakeTeaProcessor{
public MakeTeaPuer makeTeaPuer = new MakeTeaPuer();
@Override
public void process1() {
makeTeaPuer.washCupA();
}
@Override
public void process2() {
makeTeaPuer.washTeaPuer();
}
@Override
public void process3() {
makeTeaPuer.makeTeaPuer();
}
}
代码语言:javascript复制
我们定义冲泡龙井茶的任务:
代码语言:javascript复制public class LjBProcessor implements IMakeTeaProcessor{
private MakeTeaLj makeTeaLj = new MakeTeaLj();
@Override
public void process1() {
makeTeaLj.washCupB();
}
@Override
public void process2() {
makeTeaLj.washTeaLj();
}
@Override
public void process3() {
makeTeaLj.makeTeaLj();
}
}
代码语言:javascript复制
这样,我们就把泡茶的程序抽象到了一组适配接口上,方便以后更多的茶叶类型和茶具类型的扩展使用,方便泡茶任务通过接口调用而非实现调用。上面的泡茶程序是基于组合模式来实现的,是对象的适配器模式。
还有一种类适配器模式,是基于继承而非组合方式的实现,代码如下:
代码语言:javascript复制public class LjBProcessor1 extends MakeTeaLj implements IMakeTeaProcessor{
@Override
public void process1() {
super.washCupB();
}
@Override
public void process2() {
super.washTeaLj();
}
@Override
public void process3() {
super.makeTeaLj();
}
}
代码语言:javascript复制
基于对象的适配器模式更适用于接口方法和原有类方法名多数不一致的情况下。如果定义的接口方法和原有类的方法名多数一样,应该更偏向于使用类适配器。
在我们的开发中,适配器模式的使用非常广泛,主要包括的场景有:封装有缺陷的接口、统一多个类的接口、替换外部系统的接口、兼容新老版本。
在我们的日志kuan框架sel4j中,定义了抽象接口:org.apache.logging.log4j.Logger,里面定义了debug、info等各种日志打印的方法,然后针对log4j、logback等日志类进行了适配,如:Log4jLoggerAdapter
2.门面模式
门面模式提供了一组的接口的封装,使接口调用更加简单。如下面代码,客户端的一个操作要调用服务端的a、b、c3个服务,三次调用增加了通信成本,我们把接口封装成一个d接口,就降低了通信成本。
代码语言:javascript复制public interface InterfaceA {
void a();
}
public interface InterfaceB {
void b();
}
public interface InterfaceC {
void c();
}
public interface InterfaceD {
void d();
}
public class ServiceD implements InterfaceD{
private InterfaceA interfaceA;
private InterfaceB interfaceB;
private InterfaceC interfaceC;
@Override
public void d() {
interfaceA.a();
interfaceB.b();
interfaceC.c();
}
}
代码语言:javascript复制
门面模式让接口变得易用,调用成本降低。
3.适配器模式和门面模式的区别
1)解决的问题:适配器解决接口不兼容问题,而门面模式解决接口易用性问题
2)代码结构:适配器主要是使用组合加继承的方式,而门面模式其实shi是接口封装
代码地址:https://github.com/jinjunzhu/design-pattern