目录
在我们平时的开发过程中,碰到要在两个完全没有关系的类之间进行交互,第一个解决方案是修改各自类的接口,但是如果无法修改源代码或者其他原因导致无法更改接口,此时怎么办?这种情况我们往往会使用一个 Adapter ,在这两个接口之间创建一个粘合剂接口,将原本无法协作的类进行兼容,而且不用修改原来两个模块的代码,符合开闭原则。
定义
适配器模式(也叫变压器模式)是一种结构型设计模式。该模式的主要功能是将一个类与另一个类的接口匹配起来,使得这两个类的接口都可以使用同一种接口被调用。先来看一下它的定义:
- 把一个类的接口变换成客户端所期待的另一种接口,从而使原本接口不匹配而无法一起工作的两个类能够在一起工作。
适配器模式的形式分为:类的适配器模式和对象的适配器模式。本质区别是类适配器是类间的继承,而对象适配器是对象的组合关系。 不过这里讲述的是 对象的适配器模式。
适配器模式有以下三种角色:
Target 目标角色,该角色定义把其他类转换为何种接口,也就是我们的期望得到的接口。
Adaptee源角色, 现在需要适配的接口
Adapter适配器角色,适配器模式的核心角色,其他两个角色都是已经存在的角色,而适配器角色是需要新建立的,它的职责非常简单:把
Adaptee 类
通过继承或者类关联的方法转换为Target类
。
下面用C++ 代码实现大话数据模式本章代码:
class Player // Target,定义用户实际需要的接口。
{
protected:
string m_name;
public:
virtual ~Player() = default;
explicit Player(string name) :m_name(name) {}
virtual void Attack() = 0; // 攻击
virtual void Defense() = 0; // 防守
};
class Forwads :public Player
{
public:
explicit Forwads(string name) :Player(name) {}
void Attack()override
{
cout << "前锋:" << m_name << "进攻!" << endl;
}
void Defense()override
{
cout << "前锋:" << m_name << "防守!" << endl;
}
};
class Guards :public Player
{
public:
explicit Guards(string name) :Player(name) {}
void Attack()override
{
cout << "后卫:" << m_name << "进攻!" << endl;
}
void Defense()override
{
cout << "后卫:" << m_name << "防守!" << endl;
}
};
class ForeignCenter // Adaptee,此处为外籍中锋,它的接口和Target的接口不一样,是现在需要适配的接口
{
private:
string m_name;
public:
void setName(string name) { m_name = name; }
void YaoMingAttack()
{
cout << "外籍中锋:" << m_name << "进攻!" << endl;
}
void YaoMingDefense()
{
cout << "外籍中锋:" << m_name << "防守!" << endl;
}
};
class Translator : public Player // Adapter , 通过在内部包装一个 Adaptee 对象,把 Adaptee 转换成 Target
{
private:
ForeignCenter myForeignCenter;
public:
explicit Translator(string name) :Player(name)
{
myForeignCenter.setName(name);
}
void Attack()override
{
myForeignCenter.YaoMingAttack();
}
void Defense()override
{
myForeignCenter.YaoMingDefense();
}
};
int main()
{
Player *b = new Forwads("巴蒂尔");
b->Attack();
Player *m = new Guards("麦克格雷迪");
m->Attack();
Player *ym = new Translator("姚明");
ym->Attack();
ym->Defense();
delete b;
delete m;
delete ym;
b = nullptr;
m = nullptr;
ym = nullptr;
system("pause");
return 0;
}
运行后截图:
优点
更好的复用性
- 系统需要使用现有的类,而此类的接口不符合系统的需要。那么通过适配器模式就可以让这些功能得到更好的复用。
透明、简单
- 客户端可以调用同一接口,因而对客户端来说是透明的。这样做更简单 ,也更直接
更好的扩展性
- 在实现适配器功能的时候,可以调用自己开发的功能,从而自然地扩展系统的功能。
解耦性
- 通过引入适配器,可以复用现有的类,而不需要修改源代码, 将目标类和被适配者解耦, 解决了接口和复用环境不一致的情况
符合开放-关闭原则
- 当添加一个实现Adaptee接口的新类时,不必修改Adapter,Adapter就能对这个新类的实例进行适配。
灵活性非常好
- 某一天,突然不想要适配器,没问题,删除掉这个适配器就可以了,其他的代码都不用修改,基本上就类似一个灵活的构件,想用就用,不想就卸载。
缺点
- 过多的使用适配器,会让系统非常零乱,不易整体进行把握。比如,明明看到调用的是A接口,其实内部被适配成了B接口的实现,一个系统如果太多出现这种情况,无异于一场灾难。因此如果不是很有必要,可以不使用适配器,而是直接对系统进行重构。
使用场景
- 一个程序想使用已经存在的类,但该类所实现的接口和当前程序所使用的接口不一致时,就应该考虑用适配器模式。
- 两个类的职责相同或相似,但是具有不同的接口时要使用它;
- 想要建立一个可以重复使用的类,用于与一些彼此之间没有太大联系的一些类,包括一些可能在将来引进的类一起工作;
- 双方都不太容易修改:第三方组件组件的接口,与系统接口不符
注意:要在双方都不太容易修改的时候再使用适配器模式适配,而不是一有不同是就使用它。