适配器模式 (Adapter Pattern)

  适配器(Adapter)模式定义:适配器模式把一个类的接口变换成客户端所期待的另一种接口,从而使原本接口不匹配而无法在一起工作的两个类能够在一起工作。

  适配器(Adapter)模式分类:
        1:类的适配器模式;

        2:对象的适配器模式。 

假想示例说明:假设图片出版后由总公司负责销售,总公司对于图书的销售有两种:
        1:实施一系列的促销方式,例如打折;

        2:招代理商,让代理商负责销售,给一定的折扣。 

      存在的问题两种方式都存在计算图书最终价格的方式,而不太理想的是这两个方式实现的接口并不统一,原因可能是多方面的,公司打折方式计算图书最终体格的接口为promotionPrice,而代理商方式所定义的接口为proxyPrice。现在公司想在业务上统一计算图书价格的逻辑,这样就可以应用适配器模式来解决。

      公司实施促销的业务逻辑代码:

interface IPromotion 

    {

        /// <summary>

        /// 图书正常打折后的价格接口

        /// </summary>

        /// <returns></returns>

        double promotionPrice();

    }

public class computerBook:IPromotion 

    {

        /// <summary>

        /// 计算机图书打八折后的价格

        /// </summary>

        /// <returns></returns>

        public double promotionPrice()

        {

            return bookprice * 0.8; 

        }

        /// <summary>

        /// 图书价格

        /// </summary>

        double bookprice;

        public computerBook(double _bookprice)

        {

            this.bookprice = _bookprice;

        }

    }

 给代理商销售的价格计算代码:

interface IProxy

    {

        /// <summary>

        /// 代理商打折后的价格接口

        /// </summary>

        /// <returns></returns>

        double proxyPrice();

    }

  public class diamondProxy:IProxy 

    {

        /// <summary>

        /// 钻石代理商打八折后的价格

        /// </summary>

        /// <returns></returns>

        public double proxyPrice()

        {

            return bookprice * 0.8; 

        }

        /// <summary>

        /// 图书价格

        /// </summary>

        double bookprice;

        public diamondProxy(double _bookprice)

        {

            this.bookprice = _bookprice;

        }

    }

解决方案:可以把公司的促销方式定义的接口IPromotion做为目标接口,把代理商的接口IProxy 转换成IPromotion。下面分别用类适配器和对象适配器来实现。

 第一:类适配器方式:所涉及的角色包括:

      1:目标(Target)角色:这是客户所期待的接口。因为C#不支持多继承,所以Target必须是接口,不可以是类。
      2:源(Adaptee)角色:需要适配的类。
      3:适配器(Adapter)角色:把源接口转换成目标接口。这一角色必须是类。

 类适配器结构图:

类适配器代码:

/// <summary>

    /// 类适配器,同时继承被适配者(Adaptee)和目标接口IPromotion

    /// </summary>

    public class AdapterPriceClass:diamondProxy , IPromotion 

    {

        /// <summary>

        /// 转换接口

        /// </summary>

        /// <returns>代理商打折后的图书价格</returns>

        public double promotionPrice()

        {

            return this.proxyPrice();       

        }

        /// <summary>

        /// 图书价格

        /// </summary>

        double price;

        /// <summary>

        /// 构造函数,接受图书价格,同时实例化Adaptee

        /// </summary>

        /// <param name="_price"></param>

        public AdapterPriceClass(double _price):base(_price )

        {

            this.price = _price;        

        }

    }

第二:对象适配器方式:所涉及的角色包括:

      1:目标(Target)角色:这是客户所期待的接口。目标可以是具体的或抽象的类,也可以是接口。
      2:源(Adaptee)角色:需要适配的类。
      3:适配器(Adapter)角色:通过在内部包装(Wrap)一个Adaptee对象,把源接口转换成目标接口。

对象适配器结构图

对象适配器代码:

/// <summary>

    /// 对象适配器,只继承目标接口

    /// </summary>

    public class AdapterPriceObject:IPromotion 

    {     

        /// <summary>

        /// 接口转换

        /// </summary>

        /// <returns></returns>

        public double promotionPrice()

        {

            return this.proxy.proxyPrice();            

        }        

        /// <summary>

        /// 构造函数

        /// </summary>

        /// <param name="_proxy">代理商实例</param>

        public AdapterPriceObject(IProxy _proxy)

        {

            this.proxy = _proxy;

        }

        /// <summary>

        /// 代理商实例

        /// </summary>

        IProxy proxy;

    }

客户端调用方式:

             //类适配器调用方式

            //类适配器中,被适配者一定是一个类,即一个适配器对应一个适配类,所以适配器就代表了被适配者

            //也就是说下面的AdapterPriceClass就代表了diamondProxy

            IPromotion _IPromotion = new AdapterPriceClass(100.0);

            Response.Write(_IPromotion.promotionPrice());



            //对象适配器调用方式

            //对象适配器中,被适配者可以是一个类也可以是一个接口,下面的例子中就是一个接口

            //先实例了一个代理商

            diamondProxy _proxy = new diamondProxy(100.0);

            //给适配器传递参数

            IPromotion _IPromotionObject = new AdapterPriceObject( _proxy);

            Response.Write(_IPromotionObject.promotionPrice());

类适配器与对象适配器的比较:
    1:类适配器需要继承目标接口和被适配者,而对象适配器只需要继承目标接口,这样在耦合度上对象适配器占优势;
    2:从扩展性上来看,因为类适配器方式要继承具体的被适者,上面的例子就是具体的代理商,也就是说类适配器需要为每个被适配者写一个适配器,无论被适配者是否是存在多态关系。而对象适配器则可以解决这种问题。

适用场合:

    1:系统需要使用现有的类,而此类的接口不符合系统的需要。
    2: 想要建立一个可以重复使用的类,用于与一些彼此之间没有太大关联的一些类,包括一些可能在将来引进的类一起工作。这些源类不一定有很复杂的接口。
    3:(对对象适配器而言)在设计里,需要改变多个已有子类的接口,如果使用类的适配器模式,就要针对每一个子类做一个适配器,而这不太实际。

 总结:

        本文应用适配器模式比较完美的解决了两种不同方法签名名的计算图书价格的问题。分析了类适配器和对象适配器的应用和区别。

原文地址:https://www.cnblogs.com/gzskys/p/5739025.html