说道适配器模式,可能最经典的例子就是电源适配器了。我们程序员使用的笔记本电脑多半是20V左右,而我大天朝的家庭用电是220V,如何让20V的笔记本在220V的电压下工作呢?当然是电源适配器了,将220V转换成20V的电压供笔记本使用。看看电源适配器这个名字起得,设计模式与其说来源于技术,倒不如说来源于生活。其实我们要说的适配器模式就是像这个例子一样,都是引入一个适配器的角色来调控两个不兼容的东西互通。
定义:
适配器模式(Adapter),将一个类的接口转换成客户希望的另外一个接口。Adapter模式使得原本由于接口(这里的接口指的是广义的接口,它可以表示一个方法或者方法的集合)不兼容而不能一起工作的那些类可以一起工作。
其实适配器模式分两种,分别为类适配器和对象适配器模式,但是因为类适配器一般是通过多重继承来操作的,而我们主要使用Java,他是单继承,所以我们主要来浅析对象适配器模式。
适配器模式结构图如下:
如图所示,适配器模式包含如下几个角色:
- Target(目标抽象类): 目标抽象类定义客户所需接口,可以是一个抽象类或接口,也可以是具体类。可以理解为它就是电源适配器,里面声明或者实现了将高电压转为低电压的过程。
- Adapter(适配器类): 适配器可以调用另一个接口,作为一个转换器,对Adaptee和Target进行适配,适配器类是适配器模式的核心,在对象适配器中,它通过继承Target并关联一个Adaptee对象使二者产生联系。它可以理解为Target的具体实现的核心类,适配器模式就是通过这个适配器来协调适配者(220V家庭用电)和客户端(笔记本电脑)之间的融合的。
- Adaptee(适配者类): 适配者即被适配的角色,它定义了一个已经存在的接口,这个接口需要适配,适配者类一般是一个具体类,包含了客户希望使用的业务方法,在某些情况下可能没有适配者类的源代码。
下面我们还是用代码来直观的感受一下适配器模式,感受之前可能需要复习两个跟数据结构相关的知识,分别是快速排序和二分法查找。 具体可以参考老四的这两篇《浅析数据结构排序篇之快速排序Quick Sort》、《Java十道由浅入深的面试题第一期(上) 详细解析》文章。比如说现在存在一个算法库,我们需要在不重写(也有可能算法库的算法不开源)的情况下调用库中的快速排序以及二分查找两个算法来实现对学生成绩进行排序和查找。我们通过适配器模式来实现学生成绩的数据结构。其中角色分别为: 成绩操作接口、快速排序及二分查找适配者、成绩操作适配器联合二者之间的适配。代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 |
package com.glorze.adapter; /** * 成绩操作接口,适配器模式中的抽象目标 * @ClassName ScoreOperation * @author: glorze.com * @since: 2018年9月25日 下午10:42:12 */ public interface ScoreOperation { /** * 成绩排序 * @Title: sort * @param array * @return int[] * @author: 高老四博客 * @since: 2018年9月25日 下午10:43:44 */ public int[] sort(int array[]); /** * 成绩查找 * @Title: search * @param array * @param key * @return int * @author: 高老四博客 * @since: 2018年9月25日 下午10:43:53 */ public int search(int array[],int key); } |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 |
package com.glorze.adapter; /** * 成绩操作适配器 * * @ClassName OperationAdapter * @author: glorze.com * @since: 2018年9月25日 下午10:48:37 */ public class OperationAdapter implements ScoreOperation { /** * 定义适配者QuickSort对象 */ private QuickSort sortObj; /** * 定义适配者BinarySearch对象 */ private BinarySearch searchObj; public OperationAdapter() { sortObj = new QuickSort(); searchObj = new BinarySearch(); } public int[] sort(int array[]) { // 调用适配者类QuickSort的排序方法 return sortObj.quickSort(array); } public int search(int array[], int key) { // 调用适配者类BinarySearch的查找方法 return searchObj.binarySearch(array, key); } } |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 |
package com.glorze.adapter; /** * 快速排序适配者 * @ClassName QuickSort * @author: glorze.com * @since: 2018年9月25日 下午10:45:04 */ public class QuickSort { public int[] quickSort(int array[]) { sort(array, 0, array.length - 1); return array; } public void sort(int array[], int p, int r) { int q = 0; if (p < r) { q = partition(array, p, r); sort(array, p, q - 1); sort(array, q + 1, r); } } public int partition(int[] a, int p, int r) { int x = a[r]; int j = p - 1; for (int i = p; i <= r - 1; i++) { if (a[i] <= x) { j++; swap(a, j, i); } } swap(a, j + 1, r); return j + 1; } public void swap(int[] a, int i, int j) { int t = a[i]; a[i] = a[j]; a[j] = t; } } |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 |
package com.glorze.adapter; /** * 二分查找适配者 * * @ClassName BinarySearch * @author: glorze.com * @since: 2018年9月25日 下午10:47:16 */ public class BinarySearch { public int binarySearch(int array[], int key) { int low = 0; int high = array.length - 1; while (low <= high) { int mid = (low + high) / 2; int midVal = array[mid]; if (midVal < key) { low = mid + 1; } else if (midVal > key) { high = mid - 1; } else { // 找到元素返回1 return 1; } } // 未找到元素返回-1 return -1; } } |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 |
package com.glorze.adapter; import com.glorze.util.XmlUtil; /** * 适配器模式客户端 * @ClassName Client * @author: glorze.com * @since: 2018年9月25日 下午10:54:38 */ public class Client { public static void main(String args[]) { // 针对抽象目标接口编程 ScoreOperation operation; // 读取配置文件,反射生成对象 operation = (ScoreOperation) XmlUtil.getBean(); // 定义成绩数组 int scores[] = { 84, 76, 50, 69, 90, 91, 88, 96 }; int result[]; int score; System.out.println("成绩排序结果:"); result = operation.sort(scores); // 遍历输出成绩 for (int i : scores) { System.out.print(i + ","); } System.out.println(); System.out.println("查找成绩90:"); score = operation.search(result, 90); if (score != -1) { System.out.println("找到成绩90。"); } else { System.out.println("没有找到成绩90。"); } System.out.println("查找成绩92:"); score = operation.search(result, 92); if (score != -1) { System.out.println("找到成绩92。"); } else { System.out.println("没有找到成绩92。"); } } } |
以上代码使用XmlUtil动态配置适配器类,这里不贴出,想要参考的可以文末自助下载项目源码参考。
从以上代码我们可以看到,如果需要新增其它算法,可以新增一个适配器去适配新的算法,从而原有代码无须修改,符合开放-封闭原则。关于开放-封闭原则可以参考老四的这篇《浅析设计模式第四章之开放-封闭原则》文章。
除了基本的适配器,还有一个特殊的适配器(Default Adapter Pattern):
当不需要实现一个接口所提供的所有方法时,可先设计一个抽象类实现该接口,并为接口中每个方法提供一个默认实现(空方法),那么该抽象类的子类可以选择性地覆盖父类的某些方法来实现需求,它适用于不想使用一个接口中的所有方法的情况,又称为单接口适配器模式。
这个貌似不需要举例子了。
适配器模式的优点(只针对对象适配器):
- 将目标类与适配者解耦,通过引用适配器,各自维护
- 提高适配者的复用性
- 可以方便的添加或者替换适配以适配不同的需求,符合开放-封闭原则
- 一个适配器可以将多个不同的适配者适配到体格target目标类中
- 可以适配适配者的子类,根据里氏代换原则,适配者的子类可以通过适配器来适配,毕竟适配器与适配者是关联关系。适配者在适配器中做声明和实例化。关于里氏代换原则可以参考老四的《浅析设计模式第五章之依赖倒转原则》。
适配器模式的适用场景:
- 对接系统没有源代码
- 对接的系统的接口不能符合当前系统的接口规范
- 对接系统的接口需要复用,不能对其有侵入和耦合。
适配器模式Java版项目完整源码文末自助注册小站即可免费下载。
更博不易,如果觉得文章对你有帮助并且有能力的老铁烦请赞助盒烟钱,点我去赞助。或者扫描文章下面的微信/支付宝二维码打赏任意金额,老四这里抱拳了。赞助时请备注姓名或者昵称,因为您的署名会出现在赞赏列表页面,您的赞赏钱财也会被用于小站的服务器运维上面,再次抱拳。
资源下载
隐藏内容:******,购买后可见!
下载价格:0 G币
您需要先登录后,才能购买资源
欢迎访问高老四博客(glorze.com),本站技术文章代码均为老四亲自编写或者借鉴整合,其余资源多为网络收集,如涉及版权问题请与站长联系。如非特殊说明,本站所有资源解压密码均为:glorze.com。