九.结构型设计模式——Adapter Pattern(适配器模式)
-
定义
将一个接口转换成客户想要的另一个接口,适配器模式使接口不兼容的那些类可以一起工作。
UML类图如下:
其中类的定义及关系如下:
1. Target(目标抽象类):定义客户要用的特定领域的接口。
2. Adapter(适配器-公接口):调用另一个接口,作为一个转换器。
3. Adaptee(适配器-母接口):定义一个接口,Adapter需要接入。
4. Clinet(客户调用类):协同对象符合Adapter适配器。
典型应用的顺序图如下,客户的提交消息被传到Adaptee中。
-
实例1:化学数据银行
化学药品对象通过适配器接口访问化学数据银行。类图如下:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
//目标抽象类
class ChemicalCompound
{
protected string name;
protected float boilingPoint;
protected float meltingPoint;
protected double molecularWeight;
protected string molecularFormula;
//构造函数
public ChemicalCompound(string name)
{
this.name = name;
}
//属性
public float BoilingPoint
{
get { return boilingPoint; }
}
public float MeltingPoint
{
get { return meltingPoint; }
}
public double MolecularWeight
{
get { return molecularWeight; }
}
public string MolecularFormula
{
get { return molecularFormula; }
}
}
//公接口
class Compound : ChemicalCompound
{
private ChemicalDataBank bank;
public Compound(string name)
: base(name)
{
//适配器(母接口)-化学数据银行
bank = new ChemicalDataBank();
boilingPoint = bank.GetCriticalPoint(name, "B");
meltingPoint = bank.GetCriticalPoint(name, "M");
molecularFormula = bank.GetMolecularStructure(name);
molecularWeight = bank.GetMolecularWeight(name);
}
public void Display()
{
Console.WriteLine("\n化合物:{0}--------", name);
Console.WriteLine(" 分子式:{0}", MolecularFormula);
Console.WriteLine(" 分子量:{0}", MolecularWeight);
Console.WriteLine(" 熔点:{0}", MeltingPoint);
Console.WriteLine(" 沸点:{0}", BoilingPoint);
}
}
//适配器(母接口)-化学数据银行
class ChemicalDataBank
{
//取得临界点
public float GetCriticalPoint(string compound,string point)
{
float temperature=0.0F;
if(point=="M")//临界点
{
switch(compound.ToLower())
{
case "water":
temperature=0.0F;
break;
case "benzene":
temperature=5.5F;
break;
case "alcohol":
temperature=-114.1F;
break;
}
}
else//沸点
{
switch(compound.ToLower())
{
case "water":
temperature=100.0F;
break;
case "benzene":
temperature=80.1F;
break;
case "alcohol":
temperature=78.3F;
break;
}
}
return temperature;
}
//取得分子式
public string GetMolecularStructure(string compound)
{
string structure="";
switch(compound.ToLower())
{
case "water":
structure="H2O";
break;
case "benzene":
structure="C6H6";
break;
case "alcohol":
structure="C2H6O2";
break;
}
return structure;
}
//取得分子量
public double GetMolecularWeight(string compound)
{
double weight=0.0;
switch(compound.ToLower())
{
case "water":
weight=18.015;
break;
case "benzene":
weight=78.1134;
break;
case "alcohol":
weight=46.0688;
break;
}
return weight;
}
}
//客户应用测试
class Client
{
[STAThread]
static void Main(string[] args)
{
//显示取得水的特性
Compound water = new Compound("Water");
water.Display();
//显示取得苯的特性
Compound benzene = new Compound("Benzene");
benzene.Display();
//显示取得乙醇的特性
Compound alcohol = new Compound("Alcohol");
alcohol.Display();
Console.Read();
}
}
-
实例2:清洁系统
例如有个清洁系统已经设计完成,但你想增加更多的工作,IExtra接口就是采用Adapter模式插入系统中的,类图如下:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
//假定系统原来已定义Clean接口
public interface IClean
{
void MakeClean();
}
//Office类,实现Clean接口
public class Office:IClean
{
public void MakeClean()
{
Console.WriteLine("清洁办公室");
}
}
//WorkShop类,实现Clean接口
public class WorkShop:IClean
{
public void MakeClean()
{
Console.WriteLine("清洁工作室");
}
}
//IExtra接口扩展了Clean接口
public interface IExtra : IClean
{
void TakeCare();
}
//Facility设备类实现了IExtra接口,除了清洁还要养护
public class Facility:IExtra
{
public void MakeClean()
{
Console.WriteLine("清洁设备");
}
public void TakeCare()
{
Console.WriteLine("养护照料");
}
}
//客户应用测试
class Client
{
[STAThread]
static void Main(string[] args)
{
IExtra e = new Facility();
e.MakeClean();
e.TakeCare();
IClean c = new Office();
c.MakeClean();
c = new WorkShop();
c.MakeClean();
Console.Read();
}
}
-
优势和缺陷
适配器模式可以将一个类的接口和另一个类的接口匹配起来,使用的前提是你不能或不想修改原来的适配器母接口(adaptee)。例如,你向第三方购买了一些类、控件,但是没有源程序,这时,使用适配器模式,你可以统一对象访问接口。但客户调用可能需要变动。
-
应用情景
下面的情景很适合应用适配器模式:
1. 对象需要利用现存的并且接口不兼容的类。
2. 你需要创建可重用的类以协作其他接口不一定兼容的类。
3. 你需要使用若干个现存的子类但又不想派生这些子类的每一个接口。