(learn&think)

不浮躁,不自傲,学习,思考,总结

浅谈设计模式八: 适配器模式(Adapter)

| Comments

1 实例

如果有一只乌龟走的像一只鸭子,发生声音也类似鸭子,鸭子的接口已经存在了,现在想把乌龟添加到鸭子实例的调用者里,那么调用者能如何继续用鸭子的接口去调用乌龟呢?

2 适配器模式(Adapter)

2.1 目的

把一个类的接口转换成另外一个类中用户需要的接口。适配器模式能让原本因为接口不兼容而不能一起协作的类能一起协作。

2.2 实现

适配器模式分为类适配器和对象适配器,这里使用对象适配器来实现接口不兼容的转换。代码实例在这里

TrukeyAdapter 继承于目标接口类 Duck 并保存被适配类的引用 Turkey* turkey

class TurkeyAdapter : public Duck {
 public:
  explicit TurkeyAdapter(Turkey* turkey);
  virtual ~TurkeyAdapter();
  void quack();
  void fly();
 private:
  Turkey* turkey_;
}

void TurkeyAdapter::quack() {
  turkey_->gobble();
}

void TurkeyAdapter::fly() {
  for (int i = 0; i < 5; ++i) {
    turkey_->fly();
  }
}

3 总结

3.1 适配器模式(Adapter)结构

  1. 类适配器使用多重继承来使一个接口适配另外一个:
  1. 一个对象适配器依赖于对象聚合:

3.2 组成

  • 目标对象(Target)定义客户(Client)使用的特定领域的接口。
  • 客户对象(Client)利用符合目标(Target)接口的对象协作。
  • 被适配对象(Adaptee)定义一个需要被适配的已经存在的接口。
  • 适配器(Adapter)适配被适配对象(Adaptee)接口到目标(Target)接口。

3.3 应用场景

  • 你想使用一个已经存在的类,但是它的接口不符合你的需求。
  • 你想创建一个可复用的类,并与不相关或不可预见的类(就是没有必要有兼容接口的类)合作
  • (仅适用于对象适配)你需要使用一些已经存在的子类,但是通过子类化每一个来匹配它们的接口是不实际的。一个对象适配器能适配它们的父类接口,从而子类接口得到匹配。

3.4 效果

类和对象适配器有不同的权衡。

类适配器:

  • 使用一个具体的 Adaptee 类来把 Adaptee 适配到 Target。因此,类适配器不能工作当我们想适配一个类或所有的子类时。
  • 能让 Adapter 重定义一些 Adaptee 的行为,因为 Adapter 是 Adaptee 的子类。
  • 只引入一个对象,并不需要多余的指针来间接得到 adaptee。

对象适配器

  • 能让一个 Adapter 处理多个 Adaptee——也就是,Adaptee 本身和它的所有子类(如果有的话)。Adapter 也可以一次给所有 Adpatee 添加功能。
  • 使得更难重定义 Adaptee 行为。它需要子类化 Adaptee,且使得 Adapter 引用这个子类,而不是 Adaptee 本身。

当使用适配器需要考虑的一些其他的因素:

  1. Adapter 到底做多少的适配工作? 把 Adaptee 匹配到 Traget 接口的工作量在适配器间不一样。那有一段可能工作量从最简单的接口转换到支持整个不同的操作集合。Adpater 的工作量取决于 Traget 的接口到 Adaptee 的接口的相似度。
  2. 可插入的适配器。一个类更具复用性是当你减少其他类使用它作的假设条件。通过创建接口适配到一个类中,那么你就消除了其他类看到同样接口的假设。也就是说,接口适配让我们把我们的类整合到可能需要对这些类使用不同接口的现有系统里去。
  3. 使用双向适配器提供透明性。适配器一个潜在的问题是它们不能对所有客户透明。一个被适配的对象不在与 Adaptee 的接口一致时,那么它就不能在 Adaptee 对象任意使用的地方去使用了。 双向适配器 能提供如此的透明性,当两个不同的客户需要不同方式查看同一个对象时,双向适配器非常适用。

Comments