无处不在的适配器模式

简介: 凡事都有例外,就是设计新系统的时候考虑使用第三方组件,因为没必要为了迎合第三方组件修改自己的软件设计风格,可以尝试使用适配器模式。

0x01:适配器模式简介


对于适配器相信不会陌生,生活中的例子比比皆是,像耳机转接线,充电器适配器,水管适配接口等等。通过类比很容易理解软件中的适配器模式。


客户端需要一个target(目标)接口,但是不能直接重用已经存在的adaptee(适配者)类,因为它的接口和target接口不一致,所以需要adapter(适配器)将adaptee转换为target接口。前提是target接口和已存在的适配者adaptee类所做的事情是相同或相似,只是接口不同且都不易修改。如果在设计之初,最好不要考虑这种设计模式。凡事都有例外,就是设计新系统的时候考虑使用第三方组件,因为没必要为了迎合第三方组件修改自己的软件设计风格,可以尝试使用适配器模式。


下面是一个非常典型的使用适配器模式的场景:


Sun公司在1996年公开了Java语言的数据库连接工具JDBC,JDBC使得Java语言程序能够与数据库连接,并使用SQL语言来查询和操作数据。JDBC给出一个客户端通用的抽象接口,每一个具体数据库厂商(如SQL Server、Oracle、MySQL等)的JDBC驱动软件都是一个介于JDBC接口和数据库引擎接口之间的适配器软件。抽象的JDBC接口和各个数据库引擎API之间都需要相应的适配器软件,这就是为各个不同数据库引擎准备的驱动程序。

另外一个比较典型的适配器场景J2EE规范与J2EE规范实现的服务器。SUN公司提供了一套J2EE规范,然后不同厂商根据自己的理解实现了不同的应用服务器。SUN公司提供了一套servlet api规范,然后实现这套规范的著名应用服务器有Apache Tomcat、Jetty、Oracle 的 Weblogic、IBM 的 WebSphere 等。


适配器模式的UML类图如下


微信图片_20220502085251.png


无处不在的适配器模式


从类图上看主要包含如下角色:


目标角色(target):这是客户所期待的接口。目标可以是具体的或抽象的类,也可以是接口;

适配者角色(adaptee):已存在接口(可以理解是第三方提供的接口),但是和客户端期待的接口不兼容;

适配器角色(adapter):将已有接口转换成目标接口(可以理解我方需要的接口);


0x02:适配器模式的实现


  • 类适配器模式(class adapter pattern)


通过继承进行适配(类间继承)。类适配器模式在编译时实现target(目标)接口。这种适配器模式使用了多个实现了期待的接口或者已经存在的接口的多态接口。比较典型的就是:target接口被创建为一个纯粹的接口,Java不支持多继承的语言。


Target:Target目标角色,该角色定义把其他类转换为何种接口,也就是期望接口,通常情况下是一个接口或一个抽象类,一般不会是实现类


public interface Target {
public void request();
}


Adaptee:Adaptee源角色,想把谁转换为目标角色,这个“谁”就是源角色,它是已经存在的、运行良好的类或对象


public class Adaptee {
public void specificRequest() {
        System.out.println("我是已经存在的运行良好的第三方厂商");
    }
}



Adapter:Adapter适配器角色,是适配器模式的核心角色,它的职责是通过继承或是类关联的方式把源角色转换为目标角色


public class Adapter extends Adaptee implements Target {
@Override
    public void request() {
super.specificRequest();
    }
}


ConcreteTarget:目标角色的实现类


public class ConcreteTarget implements Target {
@Override
public void request() {
System.out.println("没有增加适配器的我方普通实现逻辑");
    }
}


类适配器模式测试代码


public class Client {
public static void main(String[] args) {
//原有无适配器的业务逻辑
Target target = new ConcreteTarget();
        target.request();
//增加适配器后的业务逻辑
Target target2 = new Adapter();
        target2.request();
    }
}


  • 对象适配器模式(object adapter pattern)


通过对象层次的关联关系进行委托(对象的合成关系/关联关系)。对象适配器模式在运行时实现target(目标)接口。在这种适配器模式中,适配器包装了一个类实例。在这种情况下,适配器调用包装对象实例的方法。


Target:客户所期待的接口。目标可以是具体的或抽象的类,也可以是接口


public class Target {
public void request() {
        System.out.println("没有适配器的普通请求");
    }
}


Adaptee:需要适配的类


public class Adaptee {
public void specificRequest() {
        System.out.println("适配器类实现的特殊请求");
    }
}


Adapter:通过在内部包装一个Adaptee对象,把源接口转换成目标接口


public class Adapter extends Target {
private Adaptee adaptee = new Adaptee();
@Override
    public void request() {
//替换原理的逻辑,调用适配类的逻辑
        adaptee.specificRequest();
    }
}


对象适配器模式测试代码


public class Client {
public static void main(String[] args) {
Target target = new Adapter();
        target.request();
    }
}


  • 缺省适配器模式(default adapter pattern),也叫默认适配器模式、接口适配器模式


当不需要全部实现接口提供的方法时,可以设计一个适配器抽象类实现接口,并为接口中的每个方法提供默认方法实现或者空实现(如果大家做过GUI编程,就可以经常遇到这种实现,特别是各种控件的事件监听都提供了适配器类),抽象类的子类就可以有选择的覆盖父类的某些方法实现需求,它适用于一个接口不想使用所有的方法的情况。在java8后,接口中可以有default方法,就不需要这种缺省适配器模式了。接口中方法都设置为default,实现为空,这样同样同样可以达到缺省适配器模式同样的效果。


target:包含了很多没有实现的操作接口


public interface Target {
public abstract void operation1();
public abstract void operation2();
public abstract void operation3();
}


Adapter:默认实现了所有操作抽象类,只是所有的实现都是空实现


public abstract class DefaultAdapter implements Target{
@Override
    public void operation1() {
    }
@Override
    public void operation2() {
    }
@Override
    public void operation3() {
    }
}


测试缺省适配器模式需要用到的类(相当于GUI编程的一个组件,比如按钮Button)


public class Operator {
private Target target;
public void addOperation(Target target) {
this.target= target;
    }
public void operation1() {
        target.operation1();
    }
public void operation2() {
        target.operation2();
    }
public void operation3() {
        target.operation3();
    }
}


缺省适配器模式测试代码


public class Client{
public static void main(String[] args) {
// 原来要实现所有操作类的操作
Operator operator1= new Operator();
        operator1.addOperation(new Target() {
@Override
public void operation1() {}
@Override
public void operation2() {
System.out.println("invoke operation2");
            }
@Override
public void operation3() {}
        });
        operator1.operation2();
// 2、使用缺省适配器只需要实现需要用到的接口方法
Operator operator2 = new Operator();
        operator2.addOperation(new DefaultAdapter() {
@Override
public void operation2() {
System.out.println("invoke operation2");
            }
        });
        operator2.operation2();
    }
}


适配器模式本质上是现有的不兼容的接口转换为需要的接口。类适配器模式以继承现有类的方式转换;对象适配器模式以聚合对象实例的方式转换;接口适配器模式以实现接口的方式转换。适配器模式是在现有的类和系统都不易修改的情况下才使用,在系统设计之初慎用该设计模式。


相关文章
|
3月前
|
存储 Java 开发者
抽象类和接口,你不知道的秘密!Java编程新视角
抽象类和接口,你不知道的秘密!Java编程新视角
38 5
|
3月前
|
Java 开发者 C++
|
5月前
|
设计模式 Java 数据库
Java设计模式:桥接模式实现灵活组合,超越单一继承的设计之道(十)
Java设计模式:桥接模式实现灵活组合,超越单一继承的设计之道(十)
|
5月前
|
Java 开发者 C++
Java面向对象的终极挑战:抽象类与接口的深度解析!
【6月更文挑战第17天】在Java OOP中,抽象类和接口助力代码复用与扩展。抽象类不可实例化,提供通用框架,适合继承;接口包含纯抽象方法,支持多态与松耦合。选择抽象类用于继承已有方法和状态,接口则适用于不相关类共享行为。Java 8后接口能含默认方法,增加设计灵活性。抽象类与接口常结合使用,以实现最佳设计,如`Shape`抽象类实现`Drawable`和`Selectable`接口,展现两者协同优势。理解和熟练运用这对概念是提升代码质量的关键。
43 0
|
5月前
|
Java 开发者
Java编程秘诀:掌握抽象类与接口的终极指南!
【6月更文挑战第17天】在Java中,抽象类与接口助力构建复杂系统。以动物园管理系统为例,`Animal`抽象类定义共性(如`eat()`和`makeSound()`),狮子和大象继承并实现具体行为。接口`Performable`允许动物表演,如跳舞的大象实现该接口。抽象类提供继承基础,接口实现多态,赋能灵活可扩展的软件设计。
32 0
|
5月前
|
存储 Java 开发者
Java编程新视角:抽象类和接口,你不知道的秘密!
【6月更文挑战第17天】在Java中,抽象类与接口是抽象概念的关键工具。抽象类是不可实例化的模板,包含抽象和具体方法,适合有层次结构的继承;接口仅含抽象方法,像契约般规定实现类的行为,适用于无关对象间的统一接口。Java类单继承但可多实现接口,增加设计灵活性。理解并巧妙运用二者,能提升代码质量和可维护性。
29 0
|
6月前
|
设计模式 算法 Java
揭开访问者模式的神秘面纱-轻松增强对象行为
访问者模式是一种重要的软件设计模式,其核心思想是将操作逻辑与数据结构分离,通过引入访问者类实现对数据结构中元素的灵活操作。这种模式特别适用于处理具有复杂行为和数据结构的对象,如编译器和图形编辑器等。访问者模式不仅可以提高系统的灵活性和可扩展性,还有助于增强代码的可读性和可维护性,降低模块间的耦合度。对于软件架构师和开发人员来说,熟练掌握访问者模式具有重要的实践意义,能够帮助他们更有效地设计和开发软件系统,实现更好的系统结构和代码组织。
81 0
揭开访问者模式的神秘面纱-轻松增强对象行为
|
6月前
|
设计模式 缓存 安全
探索设计模式的魅力:从单一继承到组合模式-软件设计的演变与未来
组合模式:构建灵活树形结构的艺术。 组合模式旨在解决如何将对象组合成树形结构,隐藏具体实现,使客户端对单个对象和复合对象的使用具有一致性。通过将对象组合成树形结构,组合模式提供了层次化的结构,使系统更灵活、可扩展。 核心思想在于统一叶节点和组合节点。叶节点代表具体的对象,而组合节点则是其他对象的容器。该设计允许我们以统一的方式处理叶子和组合,简化了许多操作。实践中,组合模式适用于具有树形结构并且希望保持结构灵活的系统。它不仅提高了代码的可重用性和可维护性,还使得添加新功能变得简单,无需修改现有代码。...
93 0
想象的重要性(抽象工厂)
想象的重要性(抽象工厂)
55 0
|
6月前
|
设计模式
二十三种设计模式全面解析-抽象工厂模式:创造无限可能的工厂之道
二十三种设计模式全面解析-抽象工厂模式:创造无限可能的工厂之道