在现实生活中,我们的笔记本电脑的工作电压大多数都是20V,而我国的家庭用电是220V,如何让20V的笔记本电脑能够工作在220V的电压下工作?答案:引入一个电源适配器,俗称变压器,有了这个电源适配器,生活用电和笔记本电脑即可兼容。

在软件开发中,有时候也会存在这种不兼容的情况,我们也可以像电源适配器一样引入一个称之为适配器的角色来协调这些存在不兼容的结构,这种设计方案即称之为适配器模式。

适配器模式(Builder) 学习难度:★★☆☆☆ 使用频率:★★★★☆

一、木有源码的算法库

Background : M公司在很久以前曾经开发了一个算法库,里面包含了一些常用的算法,例如排序和查找算法,在进行各类软件开发时经常需要重用该算法库中的算法。在为某学校开发教务管理系统时,开发人员发现需要对学生成绩进行排序和查找。该系统的设计人员已经开发了一个成绩操作接口IScoreOperation,在该接口中声明了排序方法Sort(int[])和查找方法Search(int[],int)。为了提高排序和查找的效率,开发人员决定重用算法库中的快速排序算法类QuickSort和二分查找算法类BinarySearch。但是,由于某些原因,现在M公司开发人员已经找不到该算法库的源代码,无法直接通过复制合粘贴操作来重用其中的代码;部分开发人员已经针对IScoreOperation接口编写代码,如果这时再要求对该接口修改或者要求大家直接使用QuickSort类和BinarySearch类将会导致大量代码需要修改。

因此,M公司开发人员面对这个没有远吗的算法库,遇到了一个幸福而又烦恼的问题:如何在既不修改现有接口又不需要任何算法库代码的基础上实现算法库的重用?  

  通过分析,不难得知,现在M公司面对的问题有点类似于我们在最开始提到的电压问题,成绩操作接口IScoreOperation有点类似于只支持20V电压的笔记本电脑,而算法库好比220V的家庭用电,这两部分都没法再进行修改,而且它们原本是两个完全不相关的结构。

  为了让IScoreOperation接口与已有算法库一起工作,让它们在同一个系统中能够兼容,最好的实现方法是增加一个类似电源适配器一样的适配器角色,通过适配器来协调这两个原本不兼容的结构。

二、适配器模式简介

2.1 适配器模式定义

  适配器模式的实现就是把客户类的请求转化为对应适配者的相应接口的调用。也就是说:当客户类调用适配器的方法时,在适配器类的内部将调用适配者类的方法,而这个过程对于客户类来说是透明的,客户类并不直接访问适配者类。因此,适配器让那些由于接口不兼容而不能交互的类可以一起工作。

适配器(Adapter)模式:将一个接口转换成客户希望的另一个接口,使接口不兼容的那些类可以一起工作。

2.2 适配器模式主要角色

  适配器模式一般包含以下3个角色:

  (1)Target(目标抽象类):目标抽象类定义了客户所需要的接口,可以是一个抽象类或接口,也可以是一个具体的类。

  (2)Adapter(适配器类):适配器可以调用另一个接口,作为一个转换器,对Adaptee和Target进行适配。适配器类是适配者模式的核心,在适配器模式中,它通过继承Target并关联一个Adaptee对象使二者产生联系。

  (3)Adaptee(适配者类):适配者即被适配的角色,它定义了一个已经存在的接口,这个接口需要适配,一般是一个具体类,包含了客户希望使用的业务方法,在某些情况下可能没有适配者类的源代码。

三、借助适配器重用算法库

3.1 解决方案结构图

  其中,IScoreOpertion接口充当抽象目标,QuickSort和BinarySearch类充当适配者,而OperationAdapter充当适配器。

3.2 具体实现

  (1)Target(目标抽象类):

    /// <summary>
/// 目标接口:抽象成绩操作类
/// </summary>
public interface IScoreOperation
{
// 成绩排序
int[] Sort(int[] array);
// 成绩查找
int Search(int[] array, int key);
}

  (2)Adaptee(适配者类):

    /// <summary>
/// 适配者A:快速排序类
/// </summary>
public class QuickSortHelper
{
public int[] QuickSort(int[] array)
{
Sort(array, , array.Length - );
return array;
} public void Sort(int[] array, int p, int r)
{
int q = ;
if (p < r)
{
q = Partition(array, p, r);
Sort(array, p, q - );
Sort(array, q + , r);
}
} public int Partition(int[] array, int p, int r)
{
int x = array[r];
int j = p - ; for (int i = p; i <= r - ; i++)
{
if (array[i] <= x)
{
j++;
Swap(array, j, i);
}
} Swap(array,j+,r);
return j + ;
} public void Swap(int[] array, int i, int j)
{
int t = array[i];
array[i] = array[j];
array[j] = t;
}
}

  

    public class BinarySearchHelper
{
public int BinarySearch(int[] array, int key)
{
int low = ;
int high = array.Length - ; while (low <= high)
{
int mid = (low + high) / ;
int midVal = array[mid]; if (midVal < key)
{
low = mid + ;
}
else if (midVal > key)
{
high = mid - ;
}
else
{
return ; // 找到元素返回1
}
} return -; // 未找到元素返回-1
}
}

  (3)Adapter(适配器类):

    /// <summary>
/// 适配器:成绩操作适配器类
/// </summary>
public class OperationAdapter : IScoreOperation
{
private QuickSortHelper sortTarget;
private BinarySearchHelper searchTarget; public OperationAdapter()
{
sortTarget = new QuickSortHelper();
searchTarget = new BinarySearchHelper();
} public int Search(int[] array, int key)
{
return searchTarget.BinarySearch(array, key);
} public int[] Sort(int[] array)
{
return sortTarget.QuickSort(array);
}
}

  (4)Client 客户端测试代码

    public class Client
{
public static void Main(string[] args)
{
IScoreOperation operation = (IScoreOperation)AppConfigHelper.GetAdapterInstance();
if (operation == null)
{
return;
} int[] scores = { , , , , , , , };
int[] result;
int score; Console.WriteLine("测试成绩排序结果:");
result = operation.Sort(scores);
foreach (int s in result)
{
Console.Write("{0},", s.ToString());
}
Console.WriteLine(); Console.WriteLine("查找是否有90分的人:");
score = operation.Search(scores, );
if (score == -)
{
Console.WriteLine("抱歉,这个真没找到~~~");
}
else
{
Console.WriteLine("恭喜,的确存在90分选手~~~");
} Console.WriteLine("查找是否有92分的人:");
score = operation.Search(scores, );
if (score == -)
{
Console.WriteLine("抱歉,这个真没找到~~~");
}
else
{
Console.WriteLine("恭喜,的确存在92分选手~~~");
} Console.ReadKey();
}
}

  为了让系统具有良好的灵活性和可扩展性,引入了配置文件和AppConfigHelper类。

  其中,将具体的Adapter实例配置在配置文件中,如果需要使用其他的排序算法和查找算法类,可以增加一个新的适配器类,使用新的适配器来适配新的算法,原有代码无需修改。

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<appSettings>
<add key="AdapterName" value="Manulife.ChengDu.DesignPattern.Adapter.OperationAdapter, Manulife.ChengDu.DesignPattern.Adapter" />
</appSettings>
</configuration>

  AppConfigHelper主要用于读取配置文件并通过反射生成实例,可以在不修改客户端代码地情况下使用新的适配器,其具体代码如下:

    public class AppConfigHelper
{
public static string GetAdapterName()
{
string factoryName = null;
try
{
factoryName = System.Configuration.ConfigurationManager.AppSettings["AdapterName"];
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
return factoryName;
} public static object GetAdapterInstance()
{
string assemblyName = AppConfigHelper.GetAdapterName();
Type type = Type.GetType(assemblyName); var instance = Activator.CreateInstance(type);
return instance;
}
}

  编译并运行,结果如下图所示:

  

四、适配器模式小结

4.1 主要优点

  (1)将目标类和适配者类解耦,从而无须修改原有结构(只需新增一个适配器类)

  (2)增加了类的透明性(适配者类中的业务实现过程)和复用性(同一个适配者类可以在多个不同的系统中复用)

  (3)灵活性和可扩展性很好(借助配置文件和反射机制,可以方便地切换适配器,符合开闭原则)

4.2 应用场景

  (1)系统需要使用一些现有的类,而这些类的接口(例如方法名)不符合系统的需要,甚至没有这些类的源码。

  (2)想要创建一个可以复用的类,用于一些彼此之间没有太大关联的类,包括一些可能在将来引进的类一起工作。

参考资料

  刘伟,《设计模式的艺术—软件开发人员内功修炼之道》

作者:周旭龙

出处:http://edisonchou.cnblogs.com

本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文链接。

设计模式的征途—7.适配器(Adapter)模式的更多相关文章

  1. 设计模式C++描述----06.适配器(Adapter)模式

    一. 定义 适配器模式将一个类的接口转换成客户希望的另外一个接口,使得原本由于接口不兼容而不能一起工作的那些类可以一起工作. Adapter 模式的两种类别:类模式和对象模式. 二. 举例说明 实际中 ...

  2. 设计模式--适配器(Adapter)模式

    今天学习另一个设计模式,适配器(Adapter)模式,这是一个共同方向,但有特殊要求,就应用到此设计模式.写到这里,想起很久以前,有写过一篇<ASP.NET的适配器设计模式(Adapter)&g ...

  3. Ruby设计模式透析之 —— 适配器(Adapter)

    转载请注明出处:http://blog.csdn.net/sinyu890807/article/details/9400153 此为Java设计模式透析的拷贝版,专门为Ruby爱好者提供的,不熟悉R ...

  4. 【原】模式之-适配器Adapter模式

    适配器Adapter模式 适配器模式(Adapter Pattern)把一个类的接口变换成客户端所期待的的另一种接口,从而使原本因接口不匹配而无法在一起工作的两个类能够在一起工作. 模式所涉及的角色有 ...

  5. 2、适配器 adapter 模式 加个"适配器" 以便于复用 结构型设计模式

    1.什么是适配器模式? 适配器如同一个常见的变压器,也如同电脑的变压器和插线板之间的电源连接线,他们虽然都是3相的,但是电脑后面的插孔却不能直接插到插线板上. 如果想让额定工作电压是直流12伏特的笔记 ...

  6. java演示适配器(adapter)模式

    为什么要使用模式: 模式是一种做事的一种方法,也即实现某个目标的途径,或者技术. adapter模式的宗旨就是,保留现有类所提供的服务,向客户提供接口,以满足客户的需求. 类适配器:客户端定义了接口并 ...

  7. 设计模式之(二)Adapter模式

    今天学习Adapter模式,An adapter helps two incompatible interfaces to work together. This is the real world ...

  8. Java 实现适配器(Adapter)模式

    平时我们会常常碰到这种情况,有了两个现成的类,它们之间没有什么联系.可是我们如今既想用当中一个类的方法.同一时候也想用另外一个类的方法.有一个解决方法是.改动它们各自的接口.可是这是我们最不愿意看到的 ...

  9. 适配器(Adapter)模式

    适配器模式把一个类的接口变换成客户端所期待的另一种接口,从而使原本因接口不匹配而无法在一起工作的两个类能够在一起工作. 适配器模式的一些其他名称:变压器模式.转换器模式.包装(Wrapper)模式.适 ...

  10. 《图解设计模式》读书笔记1-2 Adapter模式

    目录 Adapter即适配器,可以类比为将220V的电压的电源转为5V电压的手机充电器,起转换的作用. 明确概念: Adaptee:被适配者,即220v电压的电源 Adapter:适配器,即手机充电器 ...

随机推荐

  1. Python自学笔记-列表生成式(来自廖雪峰的官网Python3)

    感觉廖雪峰的官网http://www.liaoxuefeng.com/里面的教程不错,所以学习一下,把需要复习的摘抄一下. 以下内容主要为了自己复习用,详细内容请登录廖雪峰的官网查看. 列表生成式 列 ...

  2. ubuntu环境下lnmp环境搭建(2)之Nginx

    1. ubuntu编译安装nginx http://www.cnblogs.com/zhangjun516/archive/2013/02/03/2890990.html 1. 手动编译安装 Ngin ...

  3. 多线程(RunLoop)

    1.RunLoop的概念及作用 2.RunLoop的使用 3.RunLoop的相关类 4.RunLoop的工作原理 5.小结 6.思考 什么是RunLoop? 从字面意思上是一直循环跑,事实上就是一个 ...

  4. MyBatis开发中解决返回字段不全的问题

    场景重现: mybatis 在查询的时候,可以返回Map,但是一旦这个字段为空(null)的时候,map里就没有了.我用的是mysql数据库,除了在查询语句上做ifnull判断给它默认值外,有没的别的 ...

  5. Extjs6(七)——增删查改之删除

    本文基于ext-6.0.0 页面就是前面写的那个,有不清楚的可以看一下前面写页面那篇. 一.在toolbar加一个删除按钮 { text:'删除', iconCls:'x-fa fa-times', ...

  6. 博客收藏--sailing的博客

    http://blog.sina.com.cn/sailingxr free:这个博客主页的内容不错 PC的足迹 arm与x86 浅谈PCIe体系架构 浅谈cache memory

  7. LArea插件的使用

    楼主菜鸟一枚,开发微信端三级滑动遇到的N多技术问题,与大家分享,话不多说,先上效果图: LArea插件的使用,前端部分参考如下: 关于PHP插件使用,请往下看:   1.首先在前端页面引入js样式和插 ...

  8. Tomcat 笔记-设置虚拟主机

    通过作用虚拟主机,可以使多个不同域名的网站共存于一个Tomcat中 在tomcat的server.xml文件中添加主机名: <Host name="hostname" app ...

  9. Mybatis基本用法--上

    Mybatis基本用法--上 本文只是为自己查漏补缺.全面的请看官方文档,支持中英文 原理参考:http://blog.csdn.net/luanlouis/article/details/40422 ...

  10. asp.net 第三方UI控件 Telerik KendoUI 之 TreeVIew 的用法记录

    一.前台显示 备注:一次性取出所有节点 function loadTreeData() { $.ajax({ type: 'POST', url: '@(Html.UrlHref("Scri ...