什么是“适配器”? |
小白简介:
我想要一个三头插座的充电器但是现在的插排都两头的插座所以我想要一个连接三头插座的线,那么这个使用过程就是适配器模式,其中需要的三头插座就是适配类。
适配器模式(Adapter):
将一个类的接口转换成客户希望的另一个接口。Adapter模式使得原本由于接口不兼容而不能一起工作的那些类可以一起工作。(是将原本有的接口编程客户需要的接口,如同下面的类图中:将进攻和防守的英文方法改为中文方法,这样外籍中锋就懂了)
此模式为对象适配器模式,适配器分为类适配器和对象适配器,类适配器通过对重继承对一个接口与另一个接口进行匹配,但是C#不支持多重继承,所以今天讲对象适配器。
首先我们需要先设定球员接口和前锋、中锋、后卫类
/// <summary>
/// 球员类
/// </summary>
abstract class Player
{
protected string name;
public Player(string name)
{
this.name = name;
}
//进攻和防守方法
public abstract void Attack();
public abstract void Defense();
}
//前锋
class Forwards:Player
{
//此方法是直接访问父类
public Forwards(string name)
: base(name)
{
}
public override void Attack()
{
Console.WriteLine("前锋{0} 进攻", name);
}
public override void Defense()
{
Console.WriteLine("前锋{0} 防守", name);
}
}
//中锋
class Center:Player
{
//此方法是直接访问父类
public Center(string name)
: base(name)
{
}
public override void Attack()
{
Console.WriteLine("前锋{0} 进攻", name);
}
public override void Defense()
{
Console.WriteLine("前锋{0} 防守", name);
}
}
//后卫
class Guards:Player
{
//此方法是直接访问父类
public Guards(string name)
: base(name)
{
}
public override void Attack()
{
Console.WriteLine("前锋{0} 进攻", name);
}
public override void Defense()
{
Console.WriteLine("前锋{0} 防守", name);
}
}
接着我们书写外籍中锋和翻译者类:
/// <summary>
/// 外籍中锋
/// </summary>
class ForeignCenter
{
private string name;
public string Name
{
//外籍中锋类球员的姓名故意用属性而不是构造方法来区别于前三个球员类的不同
get { return name; }
set { name = value; }
}
//以下标明“外籍中锋"只懂得中文“进攻” 和“防守”
public void 进攻()
{
Console.WriteLine("外籍中锋{0} 进攻", name);
}
public void 防守()
{
Console.WriteLine("外籍中锋{0} 防守", name);
}
}
/// <summary>
/// 翻译者类
/// </summary>
class Translator:Player
{
private ForeignCenter wjzf = new ForeignCenter();
public Translator(string name)
: base(name)
{
wjzf.Name = name;
}
public override void Attack()
{
wjzf.进攻();
}
public override void Defense()
{
wjzf.防守(); }
}
方法总结:
前面的球员和几个身份都是我们一致的接口和实现接口,然后外籍中锋的名字为了区别和其他不同所以自己构造一个姓名方法,接着方法名是中文的,在翻译者类中只是重写了球员接口的方法,将里面改为外籍中锋的方法。,然后在客户端请看:
static void Main(string[] args)
{
Player b = new Forwards("巴塞尔");
b.Attack();
Player m = new Guards("麦克地雷地");
m.Attack();
//翻译者告诉姚明,教练要求你既要“进攻” 又要“防守”
Player ym = new Translator("姚明");
ym.Attack();
ym.Defense();
Console.Read();
}
运行结果:
接下来请看桥接模式 |
首先我们先了解一下合成/聚合复用原则(CARP),尽量使用合成/聚合原则,尽量不要使用类继承:因为使用CARP有助于保持每个类被封装,并被集中在单个任务上,这样类和类继承会保持较小规模,说白了就是手机品牌应该包含手机软件,但是软件并不是手机品牌的一部分,,这样就不会将全部的信息都放在手机品牌里面,大大减少了耦合,让其分离出来。
桥接模式:
将抽象部分与它的实现部分分离,使他们都可以独立的变化。(就是让抽象类和派生类分离,原本手机软件是手机的一部分,现在让手机软件分离出来),请看类图关系:
首先写出我们的手机软件接口和两个手机软件
/// <summary>
/// 手机软件接口
/// </summary>
abstract class Soft
{
public abstract void Run();
}
/// <summary>
/// 手机游戏
/// </summary>
class Game: Soft
{
public override void Run()
{
Console.WriteLine("手运行机游戏");
}
}
/// <summary>
/// 手机音乐
/// </summary>
class Music : Soft
{
public override void Run()
{
Console.WriteLine("运行手机音乐");
}
}
接着我们写手机品牌接口和两个手机类型
/// <summary>
/// 手机品牌接口
/// </summary>
abstract class Phone
{
protected Soft soft; //连接手机软件
public void SetSoft(Soft soft)
{
this.soft=soft;
}
public abstract void Run();
}
/// <summary>
/// 手机品牌:Nokia
/// </summary>
class Nokia : Phone
{
public override void Run()
{
soft.Run();
}
}
/// <summary>
/// 手机品牌:Winphone
/// </summary>
class WinPhone:Phone
{
public override void Run()
{
soft.Run();
}
}
方法总结:
在我们的类中只写一个手机品牌的接口和手机软件的接口,并且去分别实现他们不同的子类,因为手机品牌和软件是聚合关系,所以需要在软件接口那里做一个连接。 接着看客户端:
static void Main(string[] args)
{
//实例化一个手机品牌Nokia,然后给他游戏和音乐的方法
Phone ab = new Nokia(); ;
ab.SetSoft(new Game());
ab.Run();
ab.SetSoft(new Music());
ab.Run();
//实例化一个手机品牌WinPhone,然后给他音乐和游戏的方法
Phone cd = new WinPhone();
cd.SetSoft(new Game());
cd.Run();
cd.SetSoft(new Music());
cd.Run();
Console.Read();
}
运行结果:
小结 |
相同点:
两者都是结构型设计模式,采用继承机制来组合接口或实现。
不同点:
适配器模式是将一个类的接口转换成客户希望的另一个接口。而桥接是将抽象部分与其实现部分分离,使它们都可以独立的变化。
小结:
更多的不同点和区别已经在代码和说明处给出,这样写的好处是更加深刻的体会出设计模式的细节所在,更多的还需要我们共同讨论出来哦。