这种模式很容易理解,因为现实世界充满了适配器。例如,考虑一个 USB 转以太网适配器。当我们在一端有以太网接口而在另一端有 USB 时,我们需要这个。因为它们不相容。我们使用一个将一个转换为另一个的适配器。这个例子非常类似于面向对象的适配器。在设计中,当我们有一个类 (Client) 需要某种类型的对象并且我们有一个提供相同功能但公开不同接口的对象 (Adaptee) 时,就会使用适配器。
要使用适配器:
- 客户端通过使用目标接口调用适配器的方法向适配器发出请求。
- 适配器使用适配器接口在适配器上转换该请求。
- 客户端接收调用结果并且不知道适配器的存在。
定义:
适配器模式将类的接口转换为客户端期望的另一个接口。适配器让那些因为接口不兼容而不能一起工作的类一起工作。
类图:
客户端只能看到目标接口而不是适配器。适配器实现目标接口。适配器将所有请求委托给 Adaptee。
例子:
假设您有一个带有 fly() 和 makeSound() 方法的 Bird 类。还有一个带有 squeak() 方法的 ToyDuck 类。假设您缺少 ToyDuck 对象,并且您想使用 Bird 对象来代替它们。 Birds 有一些类似的功能,但实现了不同的接口,所以我们不能直接使用它们。所以我们将使用适配器模式。在这里,我们的客户是 ToyDuck,适应者是 Bird。
下面是它的Java实现。
// Java implementation of Adapter pattern
interface Bird
{
// birds implement Bird interface that allows
// them to fly and make sounds adaptee interface
public void fly();
public void makeSound();
}
class Sparrow implements Bird
{
// a concrete implementation of bird
public void fly()
{
System.out.println("Flying");
}
public void makeSound()
{
System.out.println("Chirp Chirp");
}
}
interface ToyDuck
{
// target interface
// toyducks dont fly they just make
// squeaking sound
public void squeak();
}
class PlasticToyDuck implements ToyDuck
{
public void squeak()
{
System.out.println("Squeak");
}
}
class BirdAdapter implements ToyDuck
{
// You need to implement the interface your
// client expects to use.
Bird bird;
public BirdAdapter(Bird bird)
{
// we need reference to the object we
// are adapting
this.bird = bird;
}
public void squeak()
{
// translate the methods appropriately
bird.makeSound();
}
}
class Main
{
public static void main(String args[])
{
Sparrow sparrow = new Sparrow();
ToyDuck toyDuck = new PlasticToyDuck();
// Wrap a bird in a birdAdapter so that it
// behaves like toy duck
ToyDuck birdAdapter = new BirdAdapter(sparrow);
System.out.println("Sparrow...");
sparrow.fly();
sparrow.makeSound();
System.out.println("ToyDuck...");
toyDuck.squeak();
// toy duck behaving like a bird
System.out.println("BirdAdapter...");
birdAdapter.squeak();
}
}
输出:
Sparrow...
Flying
Chirp Chirp
ToyDuck...
Squeak
BirdAdapter...
Chirp Chirp
解释 :
假设我们有一只可以发出声音()的鸟,我们有一只可以发出吱吱声的塑料玩具鸭。现在假设我们的客户改变了要求,他希望 toyDuck 发出声音而不是 ?
简单的解决方案是,我们只需将实现类更改为新的适配器类,并告诉客户端将鸟(想要 squeak())的实例传递给该类。
之前: ToyDuck toyDuck = new PlasticToyDuck();
之后: ToyDuck toyDuck = new BirdAdapter(sparrow);
你可以看到,通过改变一行,toyDuck 现在可以做 Chirp Chirp 了!!
对象适配器与类适配器
我们上面实现的适配器模式被称为对象适配器模式,因为适配器持有一个适配器的实例。还有另一种称为类适配器模式的类型,它使用继承而不是组合,但您需要多重继承来实现它。
类适配器模式的类图:
这里不是在适配器(组合)内部有一个适配对象来利用它的功能,适配器继承了适配对象。
由于包括Java在内的许多语言都不支持多重继承,并且与许多问题相关联,因此我们没有展示使用类适配器模式的实现。
好处:
- 有助于实现可重用性和灵活性。
- 客户端类并不复杂,因为必须使用不同的接口,并且可以使用多态在不同的适配器实现之间进行交换。
缺点:
- 所有请求都被转发,因此开销略有增加。
- 有时需要沿着适配器链进行许多调整才能达到所需的类型。
参考:
Head First 设计模式(书籍)