结构型模式(一) 适配器模式(Adapter)
一、动机(Motivation)
在软件系统中,由于应用环境的变化,常常需要将“一些现存的对象”放在新的环境中应用,但是新环境要求的接口是这些现存对象所不满足的。
如何应对这种“迁移的变化”?如何既能利用现有对象的良好实现,同时又能满足新的应用环境所要求的接口?
二、意图(Intent)
将一个类的接口转换成客户希望的另一个接口。Adapter模式使得原本由于接口不兼容而不能一起工作的那些类可以一起工作。
例说Adapter应用
这种实际上是一种委派的调用,本来是发送请求给MyStack,但是MyStack实际上是委派给list去处理。MyStack在这里其实就是Adapter(适配对象),list即是Adaptee(被适配的对象),而IStack就是客户期望的接口。太直接了,没什么可说的。
三、结构(Structure)
适配器有两种结构
1、对象适配器(更常用)
对象适配器使用的是对象组合的方案,它的Adapter和Adaptee的关系是组合关系,即上面例子中MyStack和list是组合关系。
OO中优先使用组合模式,组合模式不适用再考虑继承。因为组合模式更加松耦合,而继承是紧耦合的,父类的任何改动都要导致子类的改动。
上面的例子就是对象适配器。
2、类适配器(不推荐使用)
下面的例子是类适配器。
Adapter继承了ArrayList,也继承了IStack接口,它既可以使用ArrayList里的方法,也可以使用IStack接口里的方法,这样就感觉有点不伦不类。这个类违反了类应该具有单一职责的原则,它既有ArrayList的职责,也有IStack的职责,因此这种类适配不是很常用,也不推荐使用。
注意:如果一个方法有可能要委托到2个或2个以上的对象,或者2个或2个以上的类需要委托,对于对象适配器,只需要增加几个内部的属性就可以实现适配。
而对于类适配器,因为C#中类只能是单一继承,它不能继承自2个或2个以上的类,所以类适配器这里便无法使用。
四、模式的组成
可以看出,在适配器模式的结构图有以下角色:
(1)、目标角色(Target):定义Client使用的与特定领域相关的接口。
(2)、客户角色(Client):与符合Target接口的对象协同。
(3)、被适配角色(Adaptee):定义一个已经存在并已经使用的接口,这个接口需要适配。
(4)、适配器角色(Adapte) :适配器模式的核心。它将对被适配Adaptee角色已有的接口转换为目标角色Target匹配的接口。对Adaptee的接口与Target接口进行适配.
五、 适配器模式的具体实现
实现一个对栈的操作,有一个IStact接口,里面有三个方法Push(进栈)、Pop(出栈)和GetTopItem(取最顶层元素),这个IStact接口将相当于上面的Target,想要实现进栈出栈的操作,如果自己去实现数据结构显得比较麻烦,在此可以将net提供的ArrayList类拿来一用,ArrayList类就是被适配的对象,相当于上面的Adaptee。在写一个适配类StactAdapter类完成功能就可以了。
/// <summary>
/// 栈的接口
/// </summary>
public interface IStack
{
void Push(object item);
void Pop();
Object GetTopItem();
}
/// <summary>
/// 对象适配器
/// </summary>
public class StactAdapter : IStack
{
ArrayList list;
/// <summary>
/// 构造函数中实例化ArrayList
/// </summary>
public StactAdapter()
{
list = new ArrayList();
}
/// <summary>
/// 进栈
/// </summary>
/// <param name="item">压入栈的元素</param>
public void Push(object item)
{
list.Add(item);
}
/// <summary>
/// 出栈
/// </summary>
public void Pop()
{
list.RemoveAt(list.Count - 1);
}
/// <summary>
/// 取最顶层的元素
/// </summary>
/// <returns></returns>
public Object GetTopItem()
{
return list[list.Count - 1];
}
}
/// <summary>
/// 客户调用
/// </summary>
public class App
{
static void Main(string[] args)
{
IStack myStack = new StactAdapter();
myStack.Push("oec2003");
myStack.Push("oec2004");
myStack.Push("oec2005");
myStack.Pop();
Console.WriteLine(myStack.GetTopItem());
}
}
六、适配器模式的实现要点:
1、Adapter模式主要应用于“希望复用一些现存的类,但是接口又与复用环境要求不一致的情况”,在遗留代码复用、类库迁移等方面非常有用。
2、GoF23定义了两种Adapter模式的实现结构:对象适配器和类适配器。类适配器采用“多继承”的实现方式,在C#语言中,如果被适配角色是类,Target的实现只能是接口,因为C#语言只支持接口的多继承的特性。在C#语言中类适配器也很难支持适配多个对象的情况,同时也会带来了不良的高耦合和违反类的职责单一的原则,所以一般不推荐使用。对象适配器采用“对象组合”的方式,更符合松耦合精神,对适配的对象也没限制,可以一个,也可以多个,但是,使得重定义Adaptee的行为较困难,这就需要生成Adaptee的子类并且使得Adapter引用这个子类而不是引用Adaptee本身。Adapter模式可以实现的非常灵活,不必拘泥于GoF23中定义的两种结构。例如,完全可以将Adapter模式中的“现存对象”作为新的接口方法参数,来达到适配的目的。
3、Adapter模式本身要求我们尽可能地使用“面向接口的编程”风格,这样才能在后期很方便地适配。
适配器模式用来解决现有对象与客户端期待接口不一致的问题,下面详细总结下适配器两种形式的优缺点。
1、类的适配器模式:
优点:
(1)、可以在不修改原有代码的基础上来复用现有类,很好地符合 “开闭原则”
(2)、可以重新定义Adaptee(被适配的类)的部分行为,因为在类适配器模式中,Adapter是Adaptee的子类
(3)、仅仅引入一个对象,并不需要额外的字段来引用Adaptee实例(这个即是优点也是缺点)。
缺点:
(1)、用一个具体的Adapter类对Adaptee和Target进行匹配,当如果想要匹配一个类以及所有它的子类时,类的适配器模式就不能胜任了。因为类的适配器模式中没有引入Adaptee的实例,光调用this.SpecificRequest方法并不能去调用它对应子类的SpecificRequest方法。
(2)、采用了 “多继承”的实现方式,带来了不良的高耦合。
2、对象的适配器模式
优点:
(1)、可以在不修改原有代码的基础上来复用现有类,很好地符合 “开闭原则”(这点是两种实现方式都具有的)
(2)、采用 “对象组合”的方式,更符合松耦合。
缺点:
(1)、使得重定义Adaptee的行为较困难,这就需要生成Adaptee的子类并且使得Adapter引用这个子类而不是引用Adaptee本身。
3、适配器模式使用的场景:
(1)、系统需要复用现有类,而该类的接口不符合系统的需求
(2)、想要建立一个可重复使用的类,用于与一些彼此之间没有太大关联的一些类,包括一些可能在将来引进的类一起工作。
(3)、对于对象适配器模式,在设计里需要改变多个已有子类的接口,如果使用类的适配器模式,就要针对每一个子类做一个适配器,而这不太实际。
七、.NET 中适配器模式的实现
说道适配器模式在Net中的实现就很多了,比如:System.IO里面的很多类都有适配器的影子,当我们操作文件的时候,其实里面调用了COM的接口实现。以下两点也是适配器使用的案例:
1.在.NET中复用COM对象:
COM对象不符合.NET对象的接口,使用tlbimp.exe来创建一个Runtime Callable Wrapper(RCW)以使其符合.NET对象的接口,COM Interop就好像是COM和.NET之间的一座桥梁。
2..NET数据访问类(Adapter变体):
各种数据库并没有提供DataSet接口,使用DbDataAdapter可以将任何个数据库访问/存取适配到一个DataSet对象上,DbDataAdapter在数据库和DataSet之间做了很好的适配。当然还有SqlDataAdapter类型了,针对微软SqlServer类型的数据库在和DataSet之间进行适配。
结构型模式(一) 适配器模式(Adapter)的更多相关文章
- 设计模式---结构型模式之适配器模式(Adapter Pattern)
适配器模式定义 将一个类的接口,转换成客户期望的另外一个接口.适配器让原本接口不兼容的类可以合作无间. 适配器模式主要有两种类型:对象适配器和类适配器. 在详细解释这两种类型时,解释部分重要角色.生活 ...
- Java设计模式(7)——结构型模式之适配器模式(Adapter)
一.概述 概念 其实,举个生活中的例子的话,适配器模式可以类比转接头,比如typeC和USB的转接头,把原本只能接typeC的接口,拓展为可以接普通USB:这里的转接头一方面需要查在typeC上,一方 ...
- 结构型设计模式之适配器模式(Adapter)
结构 意图 将一个类的接口转换成客户希望的另外一个接口.A d a p t e r 模式使得原本由于接口不兼容而不能一起工作的那些类可以一起工作. 适用性 你想使用一个已经存在的类,而它的接口不符合你 ...
- GoF23种设计模式之结构型模式之适配器模式
一.概述 将一个类的接口转换成客户希望的另外一个接口.适配器模式使得原本由于接口不兼容而不能一起工作的那些类可以一起工作. 二.适用性 1.你想使用一个已经存在的类,但是它的接口不符合 ...
- Java设计模式——结构型模式
Java设计模式中共有7种结构型模式:适配器模式.装饰模式.代理模式.外观模式.桥接模式.组合模式.享元模式.其中对象的适配器模式是各种模式的起源,其关系如下面的图:1.适配器模式 适配器模式将某个类 ...
- NET设计模式 第三部分 结构型模式(7):适配器模式(Adapter Pattern)
适配器模式(Adapter Pattern) ——.NET设计模式系列之八 Terrylee,2006年2月 概述 在软件系统中,由于应用环境的变化,常常需要将“一些现存的对象”放在新的环境中应用,但 ...
- 设计模式06: Adapter 适配器模式(结构型模式)
Adapter 适配器模式(结构型模式) 适配(转换)的概念无处不在:电源转接头.电源适配器.水管转接头... 动机(Motivation)在软件系统中,由于应用环境的变化,常常需要将“一些现存的对象 ...
- 初探Java设计模式2:结构型模式(代理模式,适配器模式等)
行为型模式 行为型模式关注的是各个类之间的相互作用,将职责划分清楚,使得我们的代码更加地清晰. 策略模式 策略模式太常用了,所以把它放到最前面进行介绍.它比较简单,我就不废话,直接用代码说事吧. 下面 ...
- Java设计模式之结构型模式
结构型模式,共七种:适配器模式.装饰器模式.代理模式.外观模式.桥接模式.组合模式.享元模式. 一.适配器模式: 意图: 将一个类的接口转换成客户希望的另外一个接口.Adapter 模式使得原本由于接 ...
随机推荐
- 在ensp中RSTP基础设置
为什么我们要有rstp? rstp就是stp的加强版 实验模拟内容 搭建拓扑 相关参数(实验的时候看看自己的mac地址可能与我的并不同) 我们开始配置RSTP基本功能,由于交换机默认开启MSTP,所有 ...
- 如何在一个文件中写多个Vue组件(译-有删改)
原文地址 Writing multiple Vue components in a single file 在一个文件中编写多个组件是React的模式,其中一些文件包含多个组件. 走开发过程中,有些组 ...
- 数组中重复的数字(Golang)
使用哈希表 package main import "fmt" func main() { a := [...]int{2,3,1,0,2,5,3} num := make(map ...
- Vue.js 2.x render 渲染函数 & JSX
Vue.js 2.x render 渲染函数 & JSX Vue绝大多数情况下使用template创建 HTML.但是比如一些重复性比较高的场景,需要运用 JavaScript 的完全编程能力 ...
- dubbo中使用动态代理
dubbo的动态代理也是只能代理接口 源码入口在JavassistProxyFactory中 public class JavassistProxyFactory extends AbstractPr ...
- 在Firefox中操作iframe的一个小问题
在做一个 Web 的打印功能时,需要将被打印的文档写到 iframe 的 document 中. <!doctype html> <html lang="en"& ...
- [UOJ #167]【UR #11】元旦老人与汉诺塔
题目大意:给你一个有$n$个盘子的汉诺塔状态$S$,问有多少种不同的操作方法,使得可以在$m$步以内到达状态$T$.$n,m\leqslant100$ 题解:首先可以知道的是,一个状态最多可以转移到其 ...
- 创建包含CRUD操作的Web API接口-第一部
在这里,我们将创建一个新的Web API项目,它将使用实体框架实现Get,POST.PUT和DELETE方法来实现CRUD操作. 首先,在Visual Studio 2013 for Web expr ...
- GOF 的23种JAVA常用设计模式总结 03 面向对象七大设计原则
在软件开发中,为了提高软件系统的可维护性和可复用性,增加软件的可扩展性和灵活性,程序员要尽量根据 7 条原则来开发程序,从而提高软件开发效率.节约软件开发成本和维护成本. 各位代码界的大佬们总结出的七 ...
- 缺陷的背后(三)---mysql之sql_mode为空的陷阱
导语 mysql服务器可以在不同的sql_mode模式下运行,并且可以根据sql_mode系统变量的值,为不同的客户机应用不同的模式.sql_mode会影响mysql支持的sql语法,并且会执行数据验 ...