设计模式之适配器模式(adapter pattern)
适配器主要用于接口的转换或者将接口不兼容的类对象组合在一起形成对外统一接口,是一种结构性模式,其本质是是一个中间件,适用于类及其对象。
本文希望通过简单的介绍和分析,能让读者对适配器模式有一个简单直观的认识和感知。
1.目的
对现有的类的接口进行转换以符合新的需求。
2.动机
通过转换或者组合,间接复用已有功能模块完成需求。
3.优缺点
优点:
- 提高了类的复用;
- 组合若干关联对象形成对外提供统一服务的接口;
- 扩展性、灵活性好。
缺点:
- 过多使用适配模式容易造成代码功能和逻辑意义的混淆。
- 部分语言对继承的限制,可能至多只能适配一个适配者类,而且目标类必须是抽象类。
4.分类
- 类适配器
- 对象适配器
- 接口适配器
本文主要介绍前两者。
5.主要用途及场景
该模式并不是在设计开发阶段考虑的,主要用在想要修改一个已经存在的接口,或者组合若干关联对象的时候。
- 想用一个已经存在的类,但其接口不符合需求;
- 想创建一个可以复用的类,该类可以与其他不相关的类协同工作;
- 想使用一些已经存在的子类,但是不能对每一个都进行子类化以匹配它们的接口(仅适用于对象Adapter)。对象适配器可以适配他的父类接口。
6.原理
下面是GoF介绍的典型的类适配器模式和对象适配器模式的UML类图
类适配器
原理:通过类继承实现适配,继承Target的接口,继承Adaptee的实现
对象适配器
原理:通过类对象组合实现适配
Target:
定义Client真正需要使用的接口。
Adaptee:
其中定义了一个已经存在的接口,也是我们需要进行适配的接口。
Adapter:
对Adaptee和Target的接口进行适配,保证对target中接口的调用可以间接转换为对Adaptee中接口进行调用。
7.实现
接下来先将上面的UML类图转换为两个具体的例子,然后在对每一种类型在使用一个具体例子介绍.
下面我们使用几个例子来实际体验一下代理模式的应用。
7.1 类适配器
定义目标接口类:Target
public interface Target { void request(); }
被适配的类:Adaptee
public class Adaptee { public void adapteeRequest() { System.out.println("adapteeRequest method of Adaptee! "); } }
适配类Adapter,继承Target的接口request,同时继承Adaptee的实现adapteeRequest
public class Adapter extends Adaptee implements Target { @Override public void request() { // TODO Auto-generated method stub super.adapteeRequest(); } }
演示:
public class Demo { public static void main(String [] args) { Target target = new Adapter(); target.request(); // result: adapteeRequest method of Adaptee! } }
7.2 对象适配器
从上面两张UML图中可以清楚的看出两者的区别,对象中Adapter不在继承Adaptee,而是将Adaptee作为一个数据成员组合到类定义中,从而实现对其接口的访问。
public class Adapter implements Target { private Adaptee adaptee = new Adaptee(); @Override public void request() { // TODO Auto-generated method stub adaptee.adapteeRequest(); } }
7.3 类适配器实例——排序
考虑到某系统中有对数据排序的需求,下面使用适配器看一下如何复用现有的排序接口(下面的例子仅仅为了演示,接口不具实际意义);
现有排序类:EffectiveVectorSort
public class EffectVectorSort { public void vectorSort() { System.out.println("vectorSort method of EffectVectorSort! "); } }
系统排序所需的接口类定义:DataSort
public interface DataSort { void sort(); }
定义适配器:SortAdapter
public class SortAdapter extends EffectVectorSort implements DataSort { @Override public void sort() { // TODO Auto-generated method stub super.vectorSort(); } }
演示:
public class Demo { public static void main(String [] args) { DataSort dataSort = new SortAdapter(); dataSort.sort(); // vectorSort method of EffectVectorSort! } }
7.4对象适配器实例——排序
如果系统中不仅有对向量Vector的排序,也有对元组Tuple和链表LinkList等高级数据结构的排序,那么显然通过无法通过类适配将每一个排序类的子类都继承。这里便可以用到对象适配。
需要修改7.3中的SortAdapter,同时需要定义高级数据结构的排序类。
高级数据结构排序类接口:AdvanceDataSort
public interface AdvanceDataSort { void sort(); }
链表排序类:LinkListSort
public class LinkListSort implements AdvanceDataSort { @Override public void sort() { // TODO Auto-generated method stub System.out.println("sort method of LinkListSort!"); } }
元组排序类:TupleSort
public class TupleSort implements AdvanceDataSort { @Override public void sort() { // TODO Auto-generated method stub System.out.println("sort method of TupleSort"); } }
重定义适配器:SortAdapter
public class SortAdapter implements DataSort { private EffectVectorSort vectorSort = new EffectVectorSort(); private AdvanceDataSort listSort = new LinkListSort(); private AdvanceDataSort tupleSort = new TupleSort(); @Override public void sort(String dataType) { // TODO Auto-generated method stub if(dataType == "vector") { vectorSort.vectorSort(); } else if(dataType == "linklist") { listSort.sort(); } else if(dataType == "tuple") { tupleSort.sort(); } else { System.out.println("Invalid Data Type:" + dataType); } } }
演示:
public class Demo { public static void main(String [] args) { DataSort dataSort = new SortAdapter(); dataSort.sort("vector"); // vectorSort method of EffectVectorSort! dataSort.sort("linklist"); // sort method of LinkListSort! dataSort.sort("tuple"); // sort method of TupleSort dataSort.sort("dict"); // Invalid Data Type:dict } }
限于篇幅,先介绍到这里。上面的例子都是比较直观,简单的。好好体会,将其应用到实际编程中才是我们的目的。
参考:
GoF《Design Patterns: Elements of Reusable Object-Oriented Software》
https://www.runoob.com/design-pattern/adapter-pattern.html