模式设计(七)Adapter
接下来我们要介绍结构型模式,这类模式可以将一组对象组合成更大的结构。对于结构型模式(Structural Pattern)来说主要分为两种组合方式,分别为类模式和对象模式,其主要区别为类模式描述的是如何使用继承提供更有用的程序接口,而对象模式描述的是通过使用对象组合或将对象包含在其他对象里,将对象组合成更大的结构。
下面我们来看看结构型的第一种模式适配器模式(Adapter Pattern)。
适配器模式可以将类的接口转变成客户需要的其他接口。这样做的好处就是能很好的复用已有的代码,比如很多公司都生产数据库,如Oracle,IBM,Microsoft,它们的产品各有各的特点,原生的开发方法也有很大的区别,比如Oracle提供OCI接口,Sql Server提供自己专用的API。如果都使用数据库厂商的原生开发方法,那么在Sql Server开发的软件就很难移植到其他的数据库平台上去,这无疑会限制产品的应用范围。为此很多厂商就提供了各种标准的开发接口,比如最早的ODBC,BDE,到现在的ADO.NET等等,有了这种标准的数据库开发接口,我们在一个平台上开发的产品就很容易移植到其他平台上去。
那么如何让数据库适应不同的标准API而无需大的改动呢,这就是通过适配器模式来实现了,微软最早的ODBC Driver和现在的ADO.NET就相当于一个适配器,可以让数据库适应不同的标准开发接口。这样原有的产品无需针对标准的API做任何改变,只要针对不同的开发API实现一个Dirver就可以了。
适配器模式的实现原理很简单,就是通过实现了客户指定接口的类来响应客户的请求,然后在适配器内部将请求转发给原有的API接口对应的具有类似功能的方法,然后将处理的结果整理后返回给客户。
适配器模式有类的适配器模式和对象的适配器模式两种。我们将分别讨论这两种Adapter模式。
类的Adapter模式的结构:
由图中可以看出,AdapteeXML类没有CreateFile方法,而客户期待这个方法。为了使客户能够使用AdapteeXML类,提供一个中间环节,即类Adapter类,Adapter类实现了ISerTarget接口,此接口中定义了CreateFile方法,并继承自AdapteeXML,Adapter类的CreateFile方法重新封装了AdapteeXMl的CreateXMLFile方法,实现了适配的目的。<?xml:namespace prefix = o />
因为Adapter与AdapteeXML是继承的关系,所以这决定了这个适配器模式是类的。
该适配器模式所涉及的角色包括:
目标(Target)角色:这是客户所期待的接口。因为C#不支持多继承,所以Target必须是接口,不可以是类,这里为ISerTarget。
源(Adaptee)角色:需要适配的类,这里为AdapteeXML。
适配器(Adapter)角色:把源接口转换成目标接口。这一角色必须是类。
以上例子主要是一个XML串行化实例(C#):
2 using System.IO;
3 using System.Xml.Serialization;
4 using System.Xml;
5 namespace AdapterClass
6 {
7 /**//// <summary>
8 ///============== Program Description==============
9 ///Name:AdapterClass.cs
10 ///Objective:AdapterClass
11 ///Date:2006-05-04
12 ///Written By coffee.liu
13 ///================================================
14 /// </summary>
15 class Class1
16 {
17 /**//// <summary>
18 /// 应用程序的主入口点。
19 /// </summary>
20 [STAThread]
21 static void Main(string[] args)
22 {
23 ISerTarget target=new Adapter();
24 target.CreateFile();
25 Console.WriteLine("xml file created");
26 }
27 }
28 [Serializable]
29 public class Person
30 {
31 public string Name;
32 public string Sex;
33 public DateTime Brith;
34 private int PersonID;
35 public Person()
36 {}
37 public void SetID(int ID)
38 {PersonID=ID;}
39 }
40
41 interface ISerTarget
42 {
43 void CreateFile();
44 }
45 class Adapter:AdapteeXML,ISerTarget
46 {
47 public Adapter():base()
48 {
49 }
50
51 public void CreateFile()
52 {
53 this.FillData();
54 this.CreateXMLFile();
55
56 }
57
58 }
59 class AdapteeXML
60 {
61 FileStream aFile;
62 Person aPerson;
63 XmlSerializer aXML;
64 public AdapteeXML()
65 {
66 aXML=new XmlSerializer(typeof(Person));
67 aPerson=new Person();
68 }
69 public void FillData()
70 {
71 aPerson.Name="XML";
72 aPerson.Sex="男";
73 aPerson.Brith=DateTime.Now;
74 aPerson.SetID(1);
75 }
76 public void CreateXMLFile()
77 {
78 try
79 {
80 aFile=new FileStream("Person.xml",FileMode.Create);
81 }
82 catch(IOException e)
83 {
84 Console.WriteLine("some error happened!");
85 Console.WriteLine(e.ToString());
86 return;
87 }
88 aXML.Serialize(aFile,aPerson);
89 aFile.Close();
90 }
91 }
92}
93
当然用Pascal也可以实现同样的功能,我们看看如何将上面的XML文件读取出来,这里只给出功能性代码,具体扩展希望读者自己思考。
代码(Pascal):
2 //============== Program Description ==============
3 // Name:AdapterClass.dpr
4 // Objective:AdapterClass
5 // Date: 2006 - 05 - 04
6 // Written By coffee.liu
7 //================================================
8 {$APPTYPE CONSOLE}
9
10 uses
11 SysUtils,msxmldom,XMLDoc, oxmldom, xmldom, XMLIntf,windows;
12 type ISerTarget = interface
13 function ReadFile():string;
14 end ;
15 type AdapteeXML = class
16 xmlDocument1 :IXMLDocument;
17 node:IXMLNode;
18 count : Integer ;
19 constructor Create ;
20 function ReadFromXml(Node:IXMLNode; Count : integer ):string;
21 end ;
22 type Adapter = class(AdapteeXML,ISerTarget)
23 private
24 FRefCount : Integer ;
25 public
26 function QueryInterface(const IID:TGUID;out Obj):HRESULT;stdcall;
27 function _AddRef: Integer ;stdcall;
28 function _Release: Integer ;stdcall; // 这里让接口引用计数器来控制对象生存周期了,这里我为了少写代码没有人为的去控制对象的生存周期,当然读者也可以通过继承TInterfacedObject对象来完成IInterface。 function ReadFile():string;
29 constructor Create ;
30 end ;
31
32
33 { AdapteeXML }
34
35 constructor AdapteeXML. Create ;
36 begin
37 xmlDocument1 : = TXMLDocument. Create (nil);
38 xmlDocument1.LoadFromFile( ' D:\coffee.liu\Delphi7\Projects\Person.xml ' ); // 上面C#例子创建出的XML文件
39 XMLDocument1.Active: = true;
40 count : = xmlDocument1.ChildNodes. Count ;
41 node: = xmlDocument1.DocumentElement;
42 end ;
43
44 function AdapteeXML.ReadFromXml(Node:IXMLNode; Count : integer ):string;
45 var
46 i: integer ;
47 begin
48 for i : = 1 to Count - 1 do begin
49 if (node <> nil) then
50 result: = Node.ChildNodes [ 'Name' ] . Text + ' ; ' + Node.ChildNodes [ 'Sex' ] . Text + ' ; ' + Node.ChildNodes [ 'Brith' ] . Text ;
51
52 end ;
53 xmlDocument1.Active: = false;
54 end ;
55 { Adapter }
56
57 function Adapter._AddRef: Integer ;
58 begin
59 Result : = InterlockedIncrement(FRefCount);
60 end ;
61
62 function Adapter._Release: Integer ;
63 begin
64 Result : = InterlockedDecrement(FRefCount);
65 if Result = 0 then
66 Destroy;
67 end ;
68
69 function Adapter.QueryInterface(const IID: TGUID; out Obj): HRESULT;
70 begin
71 if GetInterface(IID, Obj) then
72 Result : = 0
73 else
74 Result : = E_NOINTERFACE;
75 end ;
76 function Adapter.ReadFile():string;
77 begin
78 self.ReadFromXml(self.node,self. count );
79 end ;
80 constructor Adapter. Create ;
81 begin
82 inherited;
83 end ;
84 var
85 aAdapter:Adapter;
86 begin
87 aAdapter: = Adapter. Create ;
88 Writeln(aAdapter.ReadFile);
89 end .
90
对象的Adapter模式的结构:
从图中可以看出:客户端需要调用CreateFile方法,而AdapteeXML,AdapteeSoap,AdapteeBin没有该方法,为了使客户端能够使用AdapteeXXX类,需要提供一个包装(Wrapper)类Adapter。这个包装类包装AdapteeXXX的实例,从而将客户端与AdapteeXXX衔接起来。
由于Adapter与AdapteeXXX是委派关系,这决定了这个适配器模式是对象的。
该适配器模式所涉及的角色包括:
目标(Target)角色:这是客户所期待的接口。目标可以是具体的或抽象的类,也可以是接口,这里为ISerTarget接口。
源(Adaptee)角色:需要适配的类,这里为AdapteeXML,AdapteeSoap,AdapteeBin类。
适配器(Adapter)角色:通过在内部包装(Wrap)Adaptee对象,把源接口转换成目标接口。
以上的例子主要为将Person类串行化为不同的形式的例子,这里分别保存为xml,soap,binary形式。由于我们使用了Adapter进行了统一封装,这样用户可以不必知道具体的封装细节,运用统一的CreateFile方法即可将Person类串行化为不同的形式。
具体代码(C#):
2 using System.IO;
3 using System.Xml.Serialization;
4 using System.Xml;
5 using System.Runtime.Serialization.Formatters.Binary;
6 /**/ /////运行时要将System.Runtime.Serialization.Formatters.Soap.dll的引用添加到项目中
7 using System.Runtime.Serialization.Formatters.Soap;
8
9 namespace AdapterObject
10 {
11 /**//// <summary>
12 ///============== Program Description==============
13 ///Name:AdapterObject.cs
14 ///Objective:AdapterObject
15 ///Date:2006-05-04
16 ///Written By coffee.liu
17 ///================================================
18 /// </summary>
19 class Class1
20 {
21 [STAThread]
22 static void Main(string[] args)
23 {
24 ISerTarget target=new Adapter("XML");
25 target.CreateFile();
26 Console.WriteLine("XML file created");
27
28 target=new Adapter("Soap");
29 target.CreateFile();
30 Console.WriteLine("Soap file created");
31
32 target=new Adapter("Bin");
33 target.CreateFile();
34 Console.WriteLine("Bin file created");
35 }
36 }
37 [Serializable]
38 public class Person{
39 public string Name;
40 public string Sex;
41 public DateTime Brith;
42 private int PersonID;
43 public Person()
44 {}
45 public void SetID(int ID)
46 {PersonID=ID;}
47 }
48
49 interface ISerTarget{
50 void CreateFile();
51 }
52 class Adapter:ISerTarget{
53 private AdapteeXML aXML;
54 private AdapteeSoap aSoap;
55 private AdapteeBin aBin;
56 private string S;
57 public Adapter(string s){
58 S=s;
59 if (s=="XML")
60 {
61 aXML=new AdapteeXML();
62 aXML.FillData();
63 }
64 else
65 if (s=="Soap")
66 {
67 aSoap=new AdapteeSoap();
68 aSoap.FillData();
69 }
70 else if (s=="Bin")
71 {
72 aBin=new AdapteeBin();
73 aBin.FillData();
74 }
75 else
76 {
77 aXML=new AdapteeXML();
78 aXML.FillData();
79 }
80 }
81
82 public void CreateFile(){
83 if (S=="XML")
84 {
85 aXML.CreateXMLFile();
86 }
87 else
88 if (S=="Soap")
89 {
90 aSoap.CreateSoapFile();
91 }
92 else if (S=="Bin")
93 {
94 aBin.CreateBinFile();
95 }
96 else
97 {
98 aXML.CreateXMLFile();
99 }
100
101 }
102
103 }
104 class AdapteeSoap{
105 FileStream aFileSoap;
106 Person aPerson;
107 SoapFormatter aSoapFormatter;
108 public AdapteeSoap(){
109 aSoapFormatter=new SoapFormatter();
110 aPerson=new Person();
111 }
112 public void FillData()
113 {
114 aPerson.Name="Soap";
115 aPerson.Sex="男";
116 aPerson.Brith=DateTime.Now;
117 aPerson.SetID(2);
118 }
119 public void CreateSoapFile(){
120 try
121 {
122 aFileSoap=new FileStream("SoapPerson.xml",FileMode.OpenOrCreate);
123 aSoapFormatter.Serialize(aFileSoap,aPerson);
124 aFileSoap.Close();
125 }
126 catch(IOException e){
127 Console.WriteLine("some error happened");
128 Console.WriteLine(e.ToString());
129 return;
130 }
131
132 }
133 }
134 class AdapteeBin
135 {
136 FileStream aFileBin;
137 Person aPerson;
138 BinaryFormatter aBinaryFormatter;
139 public AdapteeBin()
140 {
141 aBinaryFormatter=new BinaryFormatter();
142 aPerson=new Person();
143 }
144 public void FillData()
145 {
146 aPerson.Name="Bin";
147 aPerson.Sex="男";
148 aPerson.Brith=DateTime.Now;
149 aPerson.SetID(3);
150 }
151 public void CreateBinFile()
152 {
153 try
154 {
155 aFileBin=new FileStream("BinPerson.data",FileMode.OpenOrCreate);
156 aBinaryFormatter.Serialize(aFileBin,aPerson);
157 aFileBin.Close();
158 }
159 catch(IOException e)
160 {
161 Console.WriteLine("some error happened");
162 Console.WriteLine(e.ToString());
163 return;
164 }
165
166 }
167 }
168 class AdapteeXML{
169 FileStream aFile;
170 Person aPerson;
171 XmlSerializer aXML;
172 public AdapteeXML(){
173 aXML=new XmlSerializer(typeof(Person));
174 aPerson=new Person();
175 }
176 public void FillData(){
177 aPerson.Name="XML";
178 aPerson.Sex="男";
179 aPerson.Brith=DateTime.Now;
180 aPerson.SetID(1);
181 }
182 public void CreateXMLFile(){
183 try
184 {
185 aFile=new FileStream("Person.xml",FileMode.Create);
186 }
187 catch(IOException e)
188 {
189 Console.WriteLine("some error happened!");
190 Console.WriteLine(e.ToString());
191 return;
192 }
193 aXML.Serialize(aFile,aPerson);
194 aFile.Close();
195 }
196 }
197}
198
类适配器和对象适配器之间的差别:
1.当我们想匹配一个类和它所有子类时,类适配器将不能胜任,因为在创建子类时就已定义了派生它的基类。如上第二例,要想改为类适配器模式就必须使用三个Adapter来分别封装三个Adaptee,这样做不太实际。
2.类适配器允许适配器更改某些被匹配的类的方法,同时还允许使用其他未修改的方法。
3. 对象适配器通过将子类传递给构造函数而允许匹配所有子类,如上面的第二例。
4.对象适配器要求读者将希望使用的,被匹配对象的方法提到表面上来。
转载于:https://www.cnblogs.com/coffeeliu/archive/2006/05/04/391410.html
模式设计(七)Adapter相关推荐
- 从壹开始微服务 [ DDD ] 之一 ║ D3模式设计初探 与 我的计划书
缘起 哈喽大家周四好!又是开心的一天,时间过的真快,我们的 <从壹开始 .net core 2.1 + vue 2.5 >前后端分离系列共 34 篇已经完结了,当然以后肯定还会有更新和修改 ...
- UML+模式设计概述
转自于:http://blog.csdn.net/rexuefengye/article/details/13020225 工程学:工程庞大到一定程度必须是用工程学方法,好比直接用水泥沙子建设实用的摩 ...
- 模式设计趣解——追MM篇
创建型模式? 1.FACTORY一追MM少不了请吃饭了,麦当劳的鸡翅和肯德基的鸡翅都是MM爱吃的东西,虽然口味有所不同,但不管你带MM去麦当劳或肯德基,只管向服务员说"来四个鸡翅" ...
- visual设计的界面发布到iis上显示不一样_Material Design暗夜模式设计指南
本文较长,大概需要12分钟阅读.不久前谷歌I/O大会召开,带来了对设计师来说最为重要的的暗夜模式.由于Android面对的用户相当广泛,相当严谨具体的暗夜模式设计规范已经悄然在Google Desig ...
- BLM业务模式设计之战略控制
说明:本文源自樊老师的线上课程<业务模式制胜,BLM战略规划七步法>第25集. 业务模式的"客户选择"和"价值主张"是从满足客户需求的角度所做的设计 ...
- [渝粤教育] 中北大学 商业模式设计与创新 参考 资料
教育 -商业模式设计与创新-章节资料考试资料-中北大学[] 单元测验一 1.[单选题]以下企业中,年营收从0达到1000亿,用时最短的企业是( ) A.小米 B.华为 C.苹果 D.阿里巴巴 参考资料 ...
- 众利币开发与模式设计
一.设计模式 1.什么是设计模式 设计模式是一套被反复使用.多数人知晓.代码设计经验的总结. 2.目的 设计模式的目的是为了让软件具有更好的代码重用性.可读性.可扩展性.可靠性,同时使程序间出现高内聚 ...
- 创业商业模式设计与实务
目录 一.课程内容介绍 二.课程内容思维导图 三.文字内容 一.课程内容介绍 "创业商业模式设计与实务"是在MBA课程中非常重要的一部分.创业指希望创造创新和价值,并为此投入时间. ...
- jsp的MVC模式设计用户登陆注册
jsp的MVC模式设计用户登陆注册 用户登陆功能 1.实验内容 2.实验步骤 3.实验结果 4.实验分析 用户登陆功能 通过设计用户的登陆和注册,来学习jsp的MVC模式,熟练掌握MVC模式各部分的内 ...
- 模式设计概述:代理者模式
分布式系统模式 分布式相关的模式设计有大概三种模式,分布式系统与集中式系统相比需要完全不同的软件.管道和过滤器模式,微核和代理者模式. 代理者模式 代理者模式体系结构的强制条件是 组件应该能够访问其他 ...
最新文章
- Ubuntu换回Gnome界面
- 实战:从深度学习探究计算机视觉
- linux 查看主板sn_如何使用您的工业边缘计算单元:第三章:使用你的JayBox 主板...
- 黑马vue---8-10、v-cloak、v-text、v-html、v-bind、v-on的基本使用
- 自然语言处理期末复习(2)中文分词
- DevExpress第三方控件汉化的全部代码和使用方法
- Android之记住密码与自动登陆实现
- 【渝粤教育】国家开放大学2018年秋季 1131t卫生经济学 参考试题
- eclipse添加约束文件
- java web 课程设计_javaweb期末课程设计
- mysql amd.dll 后门_DLL型后门原理及完全清除秘诀
- AcrelEMS-IDC综合能效管理系统在某大型数据中心的应用方案
- RNA 14. SCI 文章中差异表达基因之 蛋白互作网络 (PPI)
- ERROR: Cannot uninstall ‘xxx‘. It is a distutils installed project and thus we cannot accurately
- MediaWiki搭建指导
- 微信小程序:关注公众号组件自定义样式
- jq 隔行变色(横、纵)
- 百度年龄计算机在线使用,百度精准年龄计算器在线计算app
- 新年寄语+从业感受+祝大家新年快乐~
- FITURE 、百度、咕咚“围猎”智能健身镜