一、什么是适配器模式
将源对象的接口包装成需要的接口
我们通过一个适配器类,将原先不兼容的类封装进这适配器类,并暴露一个外接统一的接口以备调用。可以让原本不兼容的两个类协同工作。
UML图
Target: 定义的目标类或接口,里面有一个统一的接口名,方便外界调取
Adapter: 继承Target,获得统一接口名方法,持有需要被适配的对象,被适配对象的接口方法放到统一接口名方法.
Adaptee:被适配的对象
二、适用场景
许多原存在的代码和新写得代码接口不一样,让用户使用到统一的接口,把原先的对象通过适配器让用户统一使用,或者扩充新的功能,大多用在代码维护后期,或者借用第三方库的情况下。有点像亡羊补牢的意思。在开发过程中是不太可能用到适配器模式的,如果真的接口不同一,那不会先考虑适配,而是重构同一的代码,这样来的方便有效。只有在双方都不太容易修改的时候再使用。
三、优缺点
优点
能够将不同接口统一起来,方便管理
不破坏源代码的完整性,亦可按需调用
缺点
事后控制不如事中控制,事中控制不如事前控制。
新增了不少类和接口
四、大话中的例子
姚明去NBA打篮球的例子, 姚明起初在NBA时不会英语,就需要一个翻译,也就是适配器。一个球员基类,前锋继承球员类,后卫继承球员类,但外籍中锋 是一个单独的类,它的进攻和防守的接口不一样。需要一个翻译(适配器)类,继承球员基类,持有外籍中锋类,将外籍中锋的不同接口进行包装。外界接触到的适配器,调用的是统一的方法,但适配器里面其实是调用的 外籍中分类的接口。
五、我的例子
using System;
using System.Collections.Generic;
namespace AdapterMode
{
class Program
{
static void Main(string[] args)
{
List<Ninja> ninjas = new List<Ninja>();
for (int i = 0; i < 5; i++)
{
ninjas.Add(new NinjaTypeA("小兵" + (i + 1)));//小兵加入战场
}
Ninja naruto = new FieldCommander();
ninjas.Add(naruto);//鸣人加入战场
foreach (var item in ninjas)
{
item.Attack();//和大家统一口号
}
}
}
/// <summary>
/// 忍者
/// </summary>
interface Ninja
{
void Attack();
}
/// <summary>
/// A类忍者
/// </summary>
class NinjaTypeA : Ninja
{
string name;
public NinjaTypeA(string name)
{
this.name = name;
}
/// <summary>
/// 投掷苦无
/// </summary>
public void Attack()
{
Console.WriteLine("{0}:投掷苦无", name);
}
}
/// <summary>
/// 鸣人特别的存在
/// </summary>
class Naruto
{
string name;
public Naruto()
{
name = "鸣人";
}
/// <summary>
/// 使用的是螺旋丸
/// </summary>
public void Rasengan()
{
Console.WriteLine("{0}:螺旋丸", name);
}
}
/// <summary>
/// 战场指挥官,相当于翻译
/// </summary>
class FieldCommander : Ninja
{
Naruto naruto = new Naruto();
/// <summary>
/// 告诉鸣人发动进攻时,是使用螺旋丸
/// </summary>
public void Attack()
{
naruto.Rasengan();
}
}
}
运行结果
PS:适配器模式更多是的充当一个补救方式,在我们正常开发过程中是不建议使用的,除非 原始代码和新增代码的借口都不允许被改动,否则要统一接口还是重构一下接口名为佳,所以这样列举的例子就是为了模式而模式,并没有实际的效能,但使用方法和用途是可以理解的。