在计算机编程中,适配器模式(有时候也称包装样式或者包装)将一个类的接口适配成用户所期待的。一个适配允许通常因为接口不兼容而不能在一起工作的类工作在一起,做法是将类自己的接口包裹在一个已存在的类中。
共有两类适配器模式:
对象适配器模式
- 在这种适配器模式中,适配器容纳一个它包裹的类的实例。在这种情况下,适配器调用被包裹对象的物理实体。
类适配器模式
- 这种适配器模式下,适配器继承自已实现的类(一般多重继承)。
现实世界中的适配器(模式)
我带着一个国标插头的笔记本电脑, 来到欧洲, 想插入到欧洲标准的墙壁插座里面, 就需要用中间这个电源适配器.
面向对象的适配器
你有个老系统, 现在来了个新供应商的类, 但是它们的接口不同, 如何使用这个新供应商的类呢?
首先, 我们不想修改现有代码, 你也不能修改供应商的代码. 那么你只能写一个可以适配新供应商接口的类了:
这里, 中间的适配器实现了你的类所期待的接口, 并且可以和供应商的接口交互以便处理你的请求.
适配器可以看作是中间人, 它从客户接收请求, 并把它们转化为供应商可以理解的请求:
所有的新代码都写在适配器里面了.
鸭子的例子
有这么一句话不知道您听过没有: 如果它路像个鸭子, 叫起来也像个鸭子, 那它就是个鸭子. (例如: Python里面的duck typing)
这句话要是用来形容适配器模式就得这么改一下: 如果它走路像个鸭子, 叫起来也像个鸭子, 那么它可能是一个使用了鸭子适配器的火鸡…
鸭子接口:
namespace AdapterPattern.Abstractions
{
public interface IDuck
{
void Quack();
void Fly();
}
}
野鸭子:
using AdapterPattern.Abstractions;
namespace AdapterPattern
{
public class MallardDuck : IDuck
{
public void Fly()
{
System.Console.WriteLine("Flying");
}
public void Quack()
{
System.Console.WriteLine("Quack");
}
}
}
火鸡接口:
namespace AdapterPattern.Abstractions
{
public interface ITurkey
{
void Gobble();
void Fly();
}
}
野火鸡:
using AdapterPattern.Abstractions;
namespace AdapterPattern.Turkies
{
public class WildTurkey : ITurkey
{
public void Fly()
{
System.Console.WriteLine("Gobble gobble");
}
public void Gobble()
{
System.Console.WriteLine("I'm flying a short distance");
}
}
}
火鸡适配器:
using AdapterPattern.Abstractions;
namespace AdapterPattern.Adapters
{
public class TurkeyAdapter : IDuck
{
private readonly ITurkey turkey;
public TurkeyAdapter(ITurkey turkey)
{
this.turkey = turkey;
}
public void Fly()
{
for (int i = 0; i < 5; i++)
{
turkey.Fly();
}
}
public void Quack()
{
turkey.Gobble();
}
}
}
测试运行:
using System;
using AdapterPattern.Abstractions;
using AdapterPattern.Adapters;
using AdapterPattern.Turkies;
namespace AdapterPattern
{
class Program
{
static void Main(string[] args)
{
DuckTestDrive();
}
static void DuckTestDrive()
{
IDuck duck = new MallardDuck();
var turkey = new WildTurkey();
IDuck turkeyAdapter = new TurkeyAdapter(turkey);
System.Console.WriteLine("Turkey says.........");
turkey.Gobble();
turkey.Fly();
System.Console.WriteLine("Duck says.........");
TestDuck(duck);
System.Console.WriteLine("TurkeyAdapter says.........");
TestDuck(turkeyAdapter);
}
static void TestDuck(IDuck duck)
{
duck.Quack();
duck.Fly();
}
}
}
理解适配器模式
Client 客户实现了某种目标接口, 它发送请求到适配器, 适配器也实现了该接口, 并且适配器保留着被适配者的实例, 适配器把请求转化为可以在被适配者身上执行的一个或者多个动作.
客户并不知道有适配器做着翻译的工作.
其他:
适配器可以适配两个或者多个被适配者.
适配器也可以是双向的, 只需要实现双方相关的接口即可.
适配器模式定义
适配器模式把一个类的接口转化成客户所期待的另一个接口. 适配器让原本因接口不兼容而无法一起工作的类成功的工作在了一起.
类图:
这个图看着也很眼熟, 这两种适配器唯一的区别就是: 类适配器同时继承于目标和被适配者, 而对象适配器使用的是组合的方式来把请求传递给被适配者.
通过鸭子的例子来认识两种适配器的角色
类适配器:
类适配器里面, 客户认为它在和鸭子谈话, 目标就是鸭子类, 客户调用鸭子上面的方法. 火鸡没有和鸭子一样的方法, 但是适配器可以接收鸭子的方法调用并把该动作转化为调用火鸡上面的方法. 适配器让火鸡可以响应一个针对于鸭子的请求, 实现方法就是同时继承于鸭子类和火鸡类
对象适配器:
对象适配器里, 客户仍然认为它在和鸭子说话, 目标还是鸭子类, 客户调用鸭子类的方法, 适配器实现了鸭子类的接口, 但是当它接收到方法调用的时候, 它把该动作转化委托给了火鸡. 火鸡并没有实现和鸭子一样的接口, 多亏了适配器, 火鸡(被适配者)将会接收到客户针对鸭子接口的方法调用.
两种适配器比较:
对象适配器: 使用组合的方式, 不仅能是配一个被适配者的类, 还可以适配它的任何一个子类.
类适配器: 只能适配一个特定的类, 但是它不需要重新实现整个被适配者的功能. 而且它还可以重写被适配者的行为.
对象适配器: 我使用的是组合而不是继承, 我通过多写几行代码把事情委托给了被适配者. 这样很灵活.
类适配器: 你需要一个适配器和一个被适配者, 而我只需要一个类就行.
对象适配器: 我对适配器添加的任何行为对被适配者和它的子类都起作用.
来源
适配器模式-百度百科
使用C# (.NET Core) 实现适配器模式 (Adapter Pattern) 和外观模式 (Facade Pattern)