设计模式1-适配器模式(Adapter)

一、分类:

结构型模式

二、理解“适配”

“适配”其实就是一种转换,这种转换发生在你不想改变某个东西现有功能,但又想把这个东西用在另外的一种新场合中。

绝大多数产品在设计之时是针对某个特定使用场合的,它向这个特定的使用场合公开一些接口以使客户可以使用它。一旦其使用场合发生变化,其对外公开的接口可能就不再符合客户的需求了。但与此同时,我们不想去改变原有的产品(如果你想改变这个原有产品的实现,那就不需要使用适配了),因为它拥有很好的功能且已经在使用过程中经过了验证,这个时候我们就需要“转化”(适配)一下,提供一个适配器,将原来产品的接口转化为客户期望的接口。这样的例子在生活中很容易举证,比如我们家庭中使用的很多电器所要求的电压为220,但也有一些电器要求更低的电压,这个时候就出现了电源适配器,利用电源适配器来改变输出的电压以提供给低电压的电器使用。

二、应用场景假设

其实上面的文件已经表示出了适配器模式使用的场合了。表现在软件设计与开发中,就是由于客户需求的变化,要将一些现有的对像放在一个新场合中使用,而新场合所期望的接口是这些原有对象所不能满足的,而我们又不想(在某些情况下可能是你根本不能去改变的)改变这些原有实现。

三、意图

将一个类的接口转换为客户希望的另一个接口。Adapter使得原本由于接口不兼容而不能一起工作的那些类可以一起工作。(Gof)

四、结构

Adapter模式有两种结构,分别是类的适配与对象的适配。

1、类的适配

角色分析:
Target目标角色:这是一个接口,定义了客户所期望的操作
Adaptee源角色:这是我们原有的产品,也是需要被适配的产品
Adapter适配器角色:在Target目标角色与Adaptee源角色之间提供一种过渡,即把Adaptee源角色所提供的接口转换为Target目标角色所提供的接口。
从结构图中可以很容易的知道,适配器角色Adapter必须要继承Targe目标角色(一个接口)与源角色Adaptee。
2、对象的适配

两种结构的角色其实都是一样的,客户的调用流程也是相同的。不同之处于两者在包装Adaptee源角色时,前者(类适配)包装的是Adaptee类(因为它同时从Target与Adaptee继承而来,可想而知,类适配的Adatper必须是一个具体类,而Target只能是一个接口),后者(对象适配)则直接包装了一个源Adaptee的实例。这一点如果放在代码中,则更容易体现出来。此处的差别导致了在具体实现时各个角色的不同实现方式(以类还是以接口)。

五、代码示例

以电压的转换为例

1、类适配

/// <summary>
/// 目标接口,这是客户要使用的,客户需要30伏的电压,这里必须是一个接口,因为C#不支持多重继承
/// </summary>
public interface Target
{
         int Out();
}
/// <summary>
/// 标准电压类,提供220伏电压,这是现有对象(即源Adaptee)
/// </summary>
public class Tension
{
         public Tension()
         {
          //...
         }

         /// <summary>
         /// 输出电压
         /// </summary>
         /// <returns></returns>
         public int OutTension()
         {
          return 220 ;
         }
}

/// <summary>
/// 电源适配器类,同时继承Target与Tension(源Adaptee对象),这里必须是一个类
/// </summary>
public class PowerAdapter:Tension,Target
{
         public PowerAdapter()
         {
          //...
         }

         #region Target 成员
         public int Out()
         {
          //do something
          //...

          //call
          return base.OutTension() - 190 ;
         }
         #endregion
}

客户调用:

          Target t = new PowerAdapter() ;

          Response.Write(t.Out().ToString()) ;

输出结果:

30

2、对象适配

对象适配直接封装了一个Adaptee实例,所以,Adapter适配器就可以不需要去继承Adaptee源对象了,只需要继承Target就可以了,这个时候由于Adapter只需要从单独的Target继承,所以,Target在实现时就不局限于只是接口的规定了,它也可以是一个类。我们面边还是把它写成了接口,这没关系。

/// <summary>
/// 目标接口,这是客户要使用的,客户需要30伏的电压,这里必须是一个接口,因为C#不支持多重继承
/// </summary>
public interface Target
{
         int Out();
}
/// <summary>
/// 标准电压类,提供220伏电压,这是现有对象(即源Adaptee)
/// </summary>
public class Tension
{
         public Tension()
         {
          //...
         }

         /// <summary>
         /// 输出电压
         /// </summary>
         /// <returns></returns>
         public int OutTension()
         {
          return 220 ;
         }
}

/// <summary>
/// 电源适配器类,同时继承Target与Tension(源Adaptee对象),这里必须是一个类
/// </summary>
public class PowerAdapter:Target
{
         private Tension MyTension = new Tension() ;

         public PowerAdapter()
         {
          //...
         }

         #region Target 成员
         public int Out()
         {
          //do something
          //...

          //call
          return MyTension.OutTension() - 190 ;
         }
         #endregion
}

客户调用:

          Target t = new PowerAdapter() ;

          Response.Write(t.Out().ToString()) ;

程序输出:

30

通过对代码的分析不难发现,类适配使用的是多继承来实现适配工作,本身类继承可能会带来的一个结果就是“高耦合”,所以推荐使用对象的适配而不是类的适配,因为对象适配使用的是“对象组合”的方式,其带来的“低耦合”使系统在未来更容易扩展。

 

六、关于适配器模式的演化

 

在我们观察对象适配时,目标角色在某些情况下是完全可以被去掉了,客户只需要直接使用适配器对象就OK了。如下代码

/// <summary>
/// 标准电压类,提供220伏电压,这是现有对象(即源Adaptee)
/// </summary>
public class Tension
{
        public Tension()
        {
         //...
        }

        /// <summary>
        /// 输出电压
        /// </summary>
        /// <returns></returns>
        public int OutTension()
        {
         return 220 ;
        }
}

/// <summary>
/// 电源适配器类
/// </summary>
public class PowerAdapter
{
        private Tension MyTension = new Tension() ;

        public PowerAdapter()
        {
         //...
        }

        #region Target 成员
        public int Out()
        {
         //do something
         //...

         //call
         return MyTension.OutTension() - 190 ;
        }
        #endregion
}

客户调用:

         PowerAdapter t = new PowerAdapter() ;

         Response.Write(t.Out().ToString()) ;

程序输出:

30

这样做有一个好处就是如果Target规定了很多与Adaptee使用无关的接口,那我们可以决定我们不需要实现Target的所有规定,当然前提是Adapter已经知道了客户期望的接口并已将其实现。

此文来源:henry的空间  http://hi.baidu.com/sanlng/blog/category/%C9%E8%BC%C6%C4%A3%CA%BD/index/0
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值