设计模式之适配器模式

概述

        适配器就是将一个类的接口变换成客户端所期待的另一种接口,从而使原本因接口不匹配而无法在一起工作的两个类能够在一起工作,它包括类适配器和对象适配器;通俗的讲,比如自己有一套现成的比较成熟接口给客户提供,有个客户想使用我们的功能接口,但是他们系统现成的接口和我们提供的接口不一样,此时适配器模式就派上用场了,一种可行的方案就是在我们接口功能的基础上封装成客户的接口提供给他们使用。

        对象适配器的类图如下:

设计模式之适配器模式_第1张图片

        类适配器的类图如下:

设计模式之适配器模式_第2张图片

适配器模式包含如下角色:

    • Target:目标抽象类

    • Adapter:适配器类

    • Adaptee:适配者类

    • Client:客户类

在这里,Target这个类中的接口才是客户所期望的类。Adapter在适配器内部包装了一个被是陪对象既Adaptee,把源接口转换为目标接口。Adaptee类是需要适配的类,其中的接口并不是Client所期望的接口所以需要被适配。

对象适配器则
• 允许一个Adapter与多个Adaptee—即Adaptee本身以及它的所有子类(如果有子类的话)—同时工作。Adapter也可以一次给所有的Adaptee添加功能。
• 使得重定义Adaptee的行为比较困难。这就需要生成Adaptee的子类并且使得Adapter引用这个子类而不是引用Adaptee本身。
使用Adapter模式时需要考虑的其他一些因素有:

1) Adapter的匹配程度 对Adaptee的接口与Target的接口进行匹配的工作量各个Adapter可能不一样。工作范围可能是,从简单的接口转换(例如改变操作名 )到支持完全不同的操作集合。Adapter的工作量取决于Target接口与Adaptee接口的相似程度
2) 可插入的Adapter   当其他的类使用一个类时,如果所需的假定条件越少,这个类就更具可复用性。如果将接口匹配构建为一个类,
就不需要假定对其他的类可见的是一个相同的接口。也就是说,接口匹配使得我们可以将自己的类加入到一些现有的系统中去,
而这些系统对这个类的接口可能会有所不同。 
3) 使用双向适配器提供透明操作 使用适配器的一个潜在问题是,它们不对所有的客户都透明。被适配的对象不再兼容 Adaptee的接口,
因此并不是所有 Adaptee对象可以被使用的地方它都可以被使用。双向适配器提供了这样的透明性。
在两个不同的客户需要用不同的方式查看同一个对象时,双向适配器尤其有用。

实例

1、STL中的stack和queue(对象适配器)

        栈stack是一种先进后出(FILO)的数据接口,它只有一个出口,在STL中的定义如下:       

template  >
class stack(){
//...
}

        堆queue是一种先进先出(FIFO)的数据接口,它有两个口,一个负责进,一个负责出,在STL中的定义如下:

template  >
class queue(){
//...
}

        deque双端队列,它有两口,两口都可以进出,所以stack和queue的底层实现都依赖于deque

它们之间的关系图如下:

设计模式之适配器模式_第3张图片

2、默认适配类

C++中定义一个接口类后,通常会对这个接口类定义一个默认适配类,业务类都继承这个适配类,这种做的好处就是当对此类扩展接口时,默认适配类实现此接口,后续的业务类就可按需实现此接口,从而避免编译出错了,示例如下图所示:

设计模式之适配器模式_第4张图片

enum  messageType : std::uint8_t {
    eNullValue, //
    eCommonValue, //普通数据类型
    eConstValue,  // 常量类型
    eBoolValue, //bool类型
    eEnumValue,  //枚举类型
    eUInt32Value, //UInt32类型
    eUInt32TransValue, //UInt32的扩展类型
    eInt32Value,  //Int32类型
    eFloatValue,  //float类型
    eDoubleValue, // double类型
    eStringValue, //string类型
};

using PVariant  = std::variant;

IMessageField接口类:

class IMessageField
{
public:
    explicit IMessageField(){}
    virtual ~IMessageField() {}
    IMessageField(const IMessageField& field){
    }

public:
    virtual IMessageField* clone(const IMessageField* pSrc) const = 0;
    virtual messageType type() const = 0;
    virtual PVariant value() const = 0;
    virtual bool setValue(const PVariant& value) = 0;
    ...
    ...
};

默认适配器类CMessageFieldAdapter:

class CMessageFieldAdapter : public IMessageField
{
public:
    explicit CMessageFieldAdapter(){}
    virtual ~CMessageFieldAdapter() {}

public:
    IMessageField* clone(const IMessageField* pSrc) const override{
        assert(0);
        return nullptr;
    }
    messageType type() const override{
        return eNullValue;
    }
    PVariant value() const override{
        assert(0);
        return 0;
    }
    bool setValue(const PVariant& value) override{
        assert(0);
        return false;
    }
    ...
    ...
};

CMessageUInt32Field类,代表std::uint32_t类型:

class CMessageUInt32Field : public CMessageFieldAdapter
{
    using _base = CMessageFieldAdapter;

public:
    explicit CMessageUInt32Field(std::uint32_t value) : m_value(value){}
    CMessageUInt32Field(const CMessageUInt32Field& other)
        : _base(other){}
    virtual ~CMessageUInt32Field() {}

public:
    IMessageField* clone(const IMessageField* pSrc) const override{
        return new CMessageUInt32Field(*dynamic_cast(pSrc));
    }
    messageType type() const override{
        return eUInt32Value;
    }
    PVariant value() const override{
        return m_value;
    }
    bool setValue(const PVariant& value) override {
        assert(value.index() == 0);
        m_value = std::get(value);
        return true;
    }
    ...
    ...
private:
    std::uint32_t  m_value;
};

CMessageStringField类,代表std::string类型:

class CMessageStringField : public CMessageFieldAdapter
{
    using _base = CMessageFieldAdapter;

public:
    explicit CMessageStringField(std::string value)
        , m_value(value){}
    CMessageStringField(const CMessageStringField& other)
        : _base(other){
        this->m_value = other.m_value;
    }
    virtual ~CMessageStringField(){}

public:
    IMessageField* clone(const IMessageField* pSrc) const override{
        return new CMessageStringField(*dynamic_cast(pSrc));
    }
    messageType type() const override{
        return eStringValue;
    }
    PVariant value() const override{
        return m_value;
    }
    bool setValue(const PVariant& value) override {
        assert(value.index() == 2);
        m_value = std::get(value);
        return true;
    }
    ...
    ...
protected:
    std::string    m_value;
};

3、STL中的配接器

1) 迭代器适配器 iterator adapters  :  insert iterators, reverse iterators, stream iterators

2) 函数适配器 function adapters :  bind, bind1st, bind2nd, ptr_func, mem_func, mem_func_ref

优点

  • 客户端代码不需要了解适配器内部的实现细节,只需了解目标接口即可。
  • 提高了代码的复用性,因为可以在多个类之间共享相同的适配器代码。
  • 单一职责原则你可以将接口或数据转换代码从程序主要业务逻辑中分离。
  • 开闭原则。 只要客户端代码通过客户端接口与适配器进行交互, 你就能在不修改现有客户端代码的情况下在程序中添加新类型的适配器

缺点

  • 适配器模式增加了系统的复杂度,因为需要新建一个对象。
  • 适配器模式可能会导致类的个数膨胀。
  • 适配器模式有时候为了兼容会导致代码的可读性变差。

你可能感兴趣的:(设计模式)