设计模式之Adapter(适配器模式)

1、出现原因:

在软件系统中,由于应用环境的变化,常常需要将“一些现存的对象”放在新的环境中应用,但是新环境要求的接口是这些现存对象不满足的。(所以可以在他们之间建立一个适配器的中间类

2、意图:

将一个类的接口转换成客户希望的另一个接口。Adapter模式使得原本由接口不兼容而不能一起工作的那些类可以一起工作。(就是在新环境的 接口 和 原来的 类之间 建立一个适配器,将他们联系起来

3、两种实现适配器模式的方式

1》对象适配器

推荐使用,因为相对于下面的类继承的方式耦合度更低 

结构图:

1对象适配器采用对象组合,通过引用一个类(原来的类)与另一个类接口(实现新的环境接口) 在对象适配器中通过组合获得Adaptee对象

 

2、通过调用Adaptee对象的方法,转换后返回Target结果

2》类的多继承方式

不推荐使用,因为使用继承实现的,对象之间的耦合度 过高

 结构图:

1、类适配器通过多继承对一个接口与另一个接口进行匹配。

 

2Target定义了Client使用的与特定领域相关的接口,Client通过调用Target实现某一个特定的操作。Adaptee是一个已经存在的类,需要与Target协同工作,这个接口需要适配。Adapter适配器适配AdapteeTarget接口。在类适配器中,通过继承获得Adaptee中的方法。

 

3、.NET不支持多重继承,因此当Target是一个类,而不是一个接口时无法实现类适配器,这时需要使用对象适配器。(如果,Target是抽象类的话,就不行了,因为 适配器在 c#里面是不支持多继承的),所以Target要是 接口,因为旧的系统已经继承了

4、代码演示

实现需求如下图:

实现代码:

  1     //新的环境(.Net不支持多继承,所以要使用接口),一个新的环境一个接口(然后 适配器 实现多个接口)
  2     public interface FootballPlayer
  3     {
  4         void Attact();
  5         void Defende();
  6     }
  7 
  8     public class FQianFeng : FootballPlayer
  9     {
 10         public void Attact()
 11         {
 12             Console.WriteLine("我是足球前锋,我要进攻了");
 13         }
 14 
 15         public void Defende()
 16         {
 17             Console.WriteLine("我是足球前锋,我要防守了");
 18         }
 19     }
 20 
 21 
 22     public class FZhongFeng : FootballPlayer
 23     {
 24         public void Attact()
 25         {
 26             Console.WriteLine("我是足球中锋,我要进攻了");
 27         }
 28 
 29         public void Defende()
 30         {
 31             Console.WriteLine("我是足球中锋,我要防守了");
 32         }
 33     }
 34 
 35     public class FHouWei : FootballPlayer
 36     {
 37         public void Attact()
 38         {
 39             Console.WriteLine("我是足球后卫,我要进攻了");
 40         }
 41 
 42         public void Defende()
 43         {
 44             Console.WriteLine("我是足球后卫,我要防守了");
 45         }
 46     }
 47 
 48 
 49     //另一个新的环境 接口
 50     public interface BasketBallPlayer
 51     {
 52         void JinGong();
 53         void FangShou();
 54     }
 55 
 56     public class BQianFeng : BasketBallPlayer
 57     {
 58         public void JinGong()
 59         {
 60             Console.WriteLine("我是篮球前锋,我要进攻了");
 61         }
 62 
 63         public void FangShou()
 64         {
 65             Console.WriteLine("我是篮球前锋,我要防守了");
 66         }
 67     }
 68 
 69 
 70 
 71     //要被适配的 类(Adaptee)
 72     //中国运动员:既会 打篮球,又会  踢足球
 73     public class ChinesePlayer
 74     {
 75         public void 足球进攻()
 76         {
 77             Console.WriteLine("我是中国运动员,我要进行足球进攻了");
 78         }
 79         public void 足球防守()
 80         {
 81             Console.WriteLine("我是中国运动员,我要进行足球防守了");
 82         }
 83         public void 篮球进攻()
 84         {
 85             Console.WriteLine("我是中国的运动员,我要进行篮球进攻了");
 86         }
 87         public void 篮球防守()
 88         {
 89             Console.WriteLine("我是中国的运动员,我要进行篮球防守了");
 90         }
 91     }
 92 
 93 
 94 
 95     //下面是一个 Adapter适配器的类
 96     public class Adapter : FootballPlayer, BasketBallPlayer  
 97     {
 98         //其实这里举的例子不适当,因为 是  先有  旧的系统(Adaptee被适配对象),才有新的环境。而且是 新的环境要使用旧的系统,才不得已使用适配器模式,,而且两个 新的环境 使用的 同一 旧的系统(也就是使用的同一 对象)
 99 
100         ChinesePlayer player = new ChinesePlayer();//这里使用的是  对象组合的方式  实现的 适配器模式
101         public void Attact()
102         {
103             player.足球进攻();
104         }
105 
106         public void Defende()
107         {
108             player.足球防守();
109         }
110 
111         public void JinGong()
112         {
113             player.篮球进攻();
114         }
115 
116         public void FangShou()
117         {
118             player.篮球防守();
119         }
120     }
Adapter模式

客户端代码:

 1             //第一个环境  使用旧的对象
 2             FootballPlayer footPlayer = new FQianFeng();
 3             FootballPlayer footChinesePlayer = new Adapter();
 4             footPlayer.Attact();
 5             footPlayer.Defende();
 6             footChinesePlayer.Attact();
 7             footChinesePlayer.Defende();
 8 
 9             //第二个环境 使用旧的对象
10             BasketBallPlayer basQianFeng = new BQianFeng();
11             BasketBallPlayer basChinesePlayer = new Adapter();
12             basQianFeng.JinGong();
13             basQianFeng.FangShou();
14             basChinesePlayer.JinGong();
15             basChinesePlayer.FangShou();
客户端调用代码

其实适配器 起的 作用就是承上启下的作用。承上实现新的环境的接口,然后在新的接口对应方法的地方调用旧的对象的方法实现想要的 旧对象的 功能。

启下:通过旧对象的引用(对象组合的方式)调用对应的功能方法,或者 通过 继承 旧对象(类的多继承),然后调用 旧对象 的功能方法

5、.Net中的适配器模式

1、DataAdapter:数据适配器

 

2、DataAdpter使应用程序的数据(sql里面的)操作统一到DataSet上,而与具体的数据库类型无关(sql 是早就存在的,要将sql里面的 统一到 Dataset上面,所以就通过 DataAdapter这个中间适配器

6、实现要点

1适配器模式重在转换接口,它能够使原本不能在一起工作的两个类一起工作,所以经常用在类库复用,代码迁移等方面,有一种亡羊补牢的味道(但是,不要硬要套这种模式如果在设计一个类库的时候,就已经想到以后会进行扩展,那么就应该提前做好准备。因为这种模式毕竟是一种 弥补措施

 

2类适配器和对象适配器可以根据具体实际情况来选用,但一般情况建议使用对象适配器模式(耦合度较低)

7、效果

通过类的继承(类适配器)或者对象的组合(对象适配器)转换已有的接口为目标接口

8、适用性

1需要使用一个已经存在的类,但接口与设计要求不符

 

2、希望创建一个可以复用的类,该类可以与其他不相关的类或者是将来不可预见的类协同工作

9、总结

1Adapter模式主要应用于“希望复用一些现存的类,但是接口又与复用环境要求不一致的情况” ,在遗留代码复用、类库迁移等方面非常有用。

 

2GoF 23 定义了两种Adapter模式的实现结构:对象适配器和类适配器。但类适配器采用“多继承”的实现方式,带来了不良的高耦合,所以一般不推荐使用。对象适配器采用“对象组合”的方式,更符合松耦合精神

 

3Adapter模式可以实现的非常灵活不必拘泥于Gof23中定义的两种结构。例如,完全可以将Adapter模式中的“现存对象”作为新的接口方法参数(接口里定义一个重载方法:例如:上面那个例子:可以增加一个重载方法void AttackChineseRearguard rearguard),然后在适配器里面进行实现,执行 现存类要执行的事务),来达到适配的目的

Adapter模式本身要求我们尽可能地使用“面向接口的编程”风格(切记千万不能面向实现编程),这样才能在后期很方便地适配。

原文地址:https://www.cnblogs.com/xiaoxiaogogo/p/3585072.html