前言:
为什么之前写过Golang 版的设计模式,还在重新写Java 版?
答:因为对于我而言,当然也希望对正在学习的大伙有帮助。Java作为一门纯面向对象的语言,更适合用于学习设计模式。
为什么类图要附上uml
因为很多人学习有做笔记的习惯,如果单纯的只是放一张图片,那么学习者也只能复制一张图片,可复用性较低,附上uml,方便有新理解时,快速出新图。
不兼容结构的协调——适配器模式
适配器模式可以将一个类的接口和另一个类的接口匹配起来,而无须修改原来的适配者接口和抽象目标类接口。适配器模式定义如下:适配器模式(Adapter Pattern):将一个接口转换成客户希望的另一个接口,使接口不兼容的那些类可以一起工作,其别名为包装器(Wrapper)。适配器模式既可以作为类结构型模式,也可以作为对象结构型模式。注:在适配器模式定义中所提及的接口是指广义的接口,它可以表示一个方法或者一组方法的集合。
@startuml class Client {} class Target { + request() } class Adaptee { + specificRequest() } class Adapter { - Adaptee adaptee; + request() } note left of Adapter::request adaptee.specificRequest() end note Client -right-> Target Target --> Adapter Adapter -right-> Adaptee @end
在对象适配器模式结构图中包含以下3个角色。
(1)Target(目标抽象类):目标抽象类定义客户所需接口,可以是一个抽象类或接口,也可以是具体类。
(2)Adapter(适配器类):适配器可以调用另一个接口,作为一个转换器,对Adaptee和Target进行适配。适配器类是适配器模式的核心,在对象适配器模式中,它通过继承Target并关联一个Adaptee对象使二者产生联系。
(3)Adaptee(适配者类):适配者即被适配的角色,它定义了一个已经存在的接口,这个接口需要适配。适配者类一般是一个具体类,包含了客户希望使用的业务方法,在某些情况下可能没有适配者类的源代码。
简单代码实现
package struct; public class AdapterDemo { public static void main(String[] args) { new Adapter(new Adaptee()).request(); } static class Adaptee { public void specificRequest() { System.out.println("adapte request"); } } static class Adapter { private final Adaptee adaptee; public Adapter(Adaptee adaptee) { this.adaptee = adaptee; } public void request() { System.out.println("adapter request "); adaptee.specificRequest(); } } }
扩展
类适配器模式
除了对象适配器模式之外,适配器模式还有一种形式,那就是类适配器模式。类适配器模式与对象适配器模式最大的区别在于其适配器和适配者之间的关系是继承关系。类适配器模式结构如下所示。
@startuml class Client {} class Target { + request() } class Adaptee { + specificRequest() } class Adapter extends Adaptee{ + request() } note left of Adapter::request super.specificRequest() end note Client -right-> Target Target --> Adapter @end
双向适配器模式
在对象适配器模式的使用过程中,如果在适配器中同时包含对目标类和适配者类的引用,适配者可以通过它调用目标类中的方法,目标类也可以通过它调用适配者类中的方法,那么该适配器就是一个双向适配器。其模式结构示意图如下所示。
@startuml interface Target { + request() } interface Adaptee { + specificRequest() } class ConcreteTarget implements Target{ + request() } class ConcreteAdaptee implements Adaptee { + specificRequest() } class Adapter implements Target, Adaptee { - Adaptee adaptee; - Target target; + Adapter(Target target) + Adapter(Adaptee adaptee) + request() + specificRequest() } note left of Adapter::request adaptee.specificRequest() end note note left of Adapter::specificRequest target.request end note @end
缺省适配器模式
缺省适配器模式是适配器模式的一种变体,其应用也较为广泛。
缺省适配器模式的定义如下:缺省适配器模式(Default Adapter Pattern):当不需要实现一个接口所提供的所有方法时,可先设计一个抽象类实现该接口,并为接口中每个方法提供一个默认实现(空方法),那么该抽象类的子类可以选择性地覆盖父类的某些方法来实现需求。它适用于不想使用一个接口中的所有方法的情况,又称为单接口适配器模式。
适配器模式总结
无论是对象适配器模式还是类适配器模式都具有如下优点:
(1)将目标类和适配者类解耦。通过引入一个适配器类来重用现有的适配者类,无须修改原有结构。
(2)增加了类的透明性和复用性。将具体的业务实现过程封装在适配者类中,对于客户端类而言是透明的,而且提高了适配者类的复用性,同一个适配者类可以在多个不同的系统中复用。
(3)灵活性和扩展性都非常好。通过使用配置文件,可以很方便地更换适配器,也可以在不修改原有代码的基础上增加新的适配器类,完全符合开闭原则。
具体来说,类适配器模式还有这样的优点:由于适配器类是适配者类的子类,因此可以在适配器类中置换一些适配者的方法,使得适配器的灵活性更强。
对象适配器模式还有如下优点:(1)一个对象适配器可以把多个不同的适配者适配到同一个目标。(2)可以适配一个适配者的子类。由于适配器和适配者之间是关联关系,根据里氏代换原则,适配者的子类也可通过该适配器进行适配。
类适配器模式的缺点如下:
(1)对于Java、C#等不支持多重类继承的语言,一次最多只能适配一个适配者类,不能同时适配多个适配者。
(2)适配者类不能为最终类,例如在Java中不能为final类,C#中不能为sealed类。
(3)在Java、C#等语言中,类适配器模式中的目标抽象类只能为接口,不能为类,其使用有一定的局限性。
对象适配器模式的缺点是:与类适配器模式相比,要在适配器中置换适配者类的某些方法比较麻烦。如果一定要置换掉适配者类的一个或多个方法,可以先做一个适配者类的子类,在子类中将适配者类的方法置换掉,然后再把适配者类的子类当作真正的适配者进行适配,实现过程较为复杂。
适用场景
在以下情况下可以考虑使用适配器模式:
(1)系统需要使用一些现有的类,而这些类的接口(例如方法名)不符合系统的需要,甚至没有这些类的源代码。
(2)想创建一个可以重复使用的类,用于与一些彼此之间没有太大关联的类,包括一些可能在将来引进的类一起工作。
🚀 作者简介:作为某云服务提供商的后端开发人员,我将在这里与大家简要分享一些实用的开发小技巧。在我的职业生涯中积累了丰富的经验,希望能通过这个博客与大家交流、学习和成长。技术栈:Java、Golang、PHP、Python、Vue、React