重复使用一个界面与你期待不同之既存类!!
官方说明
将一个类的界面,转换成另一个界面供客户使用。转接器让原本界面不相容的类可以合作无间。
主要疗效
重复使用一个界面与你期待不同之既存类透过转接器(Adapter)将不同界面类(Adaptee)转换至用户(Client)期望的目标界面(ITarget)
适用情况
1. 转换界面需求
2. 界面功能相似(有相对应功能)
关键心法
1. 建立转接器(Adapter) [继承期望的目标界面(ITarget)]
2. 透过对象合成方式包装(wrap)被转接者(Adaptee)
3. 将被转接者(Adaptee)之方法对应至目标界面(ITarget)需实践的方法中
4. 客户不须修改程序,直接以Adapter转换Adaptee,透过原本的Itarget界面来操作Adaptee对象。
应用联想
目前有一支遥控器(RemoteController)只支持操控具机械式开关(ISwitchable)台灯,随着科技日新月异,配合厂商开发了一批具触碰式开关(ITouchable)台灯,当然老板希望遥控器也能够控制新式触碰行台灯;分析一下需求,遥控器已是上市产品所以不允许被更动,开发触碰式台灯的厂商也不可能依照我们无理的要求实践ISwitchable界面来符合我们的需求,所以我们只能想办法转换ITouchable至ISwitchable界面来符合遥控器。以下我们将实际演练如何透过Adapter模式的协助,转换不同界面对象至我们期待的界面。
首先来检查是否合乎适用情境:
1. 转换界面需求
转换ITouchable至ISwitchable界面来符合遥控器操控目标之界面需求
2. 界面功能相似(有相对应功能)
ITouchable与ISwitchable界面皆有开启(On)及关闭(Off)功能
来看看原本就存在的系统类图
* 机械式台灯 (实践机械式开关界面)
interface ISwitchable
{
// 机械式开启
void SwitchOn();
// 机械式关闭
void SwitchOff();
}
class SwitchableLamp : ISwitchable
{
// 机械式开启
public void SwitchOn()
{
Console.WriteLine("开灯(使用机械式开关)");
}
// 机械式关闭
public void SwitchOff()
{
Console.WriteLine("关灯(使用机械式开关)");
}
}
* 遥控器
class RemoteController
{
// 只支持操控"具有机械式开关特性"对象
ISwitchable _switchableLamp;
// 注入"具有机械式开关特性"对象实例
public RemoteController(ISwitchable switchableLamp)
{
_switchableLamp = switchableLamp;
}
// 开启
public void TurnOn()
{
if (_switchableLamp != null)
{ _switchableLamp.SwitchOn(); }
}
// 关闭
public void TurnOff()
{
if (_switchableLamp != null)
{ _switchableLamp.SwitchOff(); }
}
}
*使用者操作方式
static void Main(string[] args)
{
// 遥控器
RemoteController remoteController;
Console.WriteLine("使用遥控器控制 机械式式台灯:");
// 建立机械式开关台灯
ISwitchable switchableLight = new SwitchableLamp();
// 注入遥控器欲操控之台灯实例 (符合ISwitchable界面)
remoteController = new RemoteController(switchableLight);
// 开启
remoteController.TurnOn();
// 关闭
remoteController.TurnOff();
Console.ReadKey();
}
输出结果
接着新式触碰式台灯来了。
interface ITouchable
{
// 触碰式开启
void TouchOn();
// 触碰式关闭
void TouchOff();
}
class TouchableLamp : ITouchable
{
// 触碰式开启
public void TouchOn()
{
Console.WriteLine("开灯(使用触碰式开关)");
}
// 触碰式关闭
public void TouchOff()
{
Console.WriteLine("关灯(使用触碰式开关)");
}
}
首先面对的问题是,RemoteController无法注入非实践ISwitchable界面的台灯,所以我们是无法达到老板的需求利用同一支遥控器来控制新型触碰式台灯。所以笔者将搭配关键心法来实现界面转换器(Adapter),让新型触碰式台灯也列入遥控器支持范围。
1. 建立转接器(SwitchableLampAdapter) [继承期望的目标界面(ISwitchable)]
class SwitchableLampAdapter : ISwitchable
{
// 机械式开启
public void SwitchOn()
{
throw new NotImplementedException();
}
// 机械式关闭
public void SwitchOff()
{
throw new NotImplementedException();
}
}
2. 透过对象合成方式包裹(wrap)被转接者(ITouchable/TouchableLamp)
class SwitchableLampAdapter : ISwitchable
{
// 支持转换"具有触碰式开关特性"对象
ITouchable _touchableLamp;
// 注入"具有触碰式开关特性"对象实例
public SwitchableLampAdapter(ITouchable touchableLamp)
{
_touchableLamp = touchableLamp;
}
// 机械式开启
public void SwitchOn()
{
throw new NotImplementedException();
}
// 机械式关闭
public void SwitchOff()
{
throw new NotImplementedException();
}
}
3. 将被转接者(ITouchable/TouchableLamp)之方法对应至目标界面(ISwitchable)需实践的方法中
class SwitchableLampAdapter : ISwitchable
{
// 支持转换"具有触碰式开关特性"对象
ITouchable _touchableLamp;
// 注入"具有触碰式开关特性"对象实例
public SwitchableLampAdapter(ITouchable touchableLamp)
{
_touchableLamp = touchableLamp;
}
// 机械式开启
public void SwitchOn()
{
// 触碰式开启
_touchableLamp.TouchOn();
}
// 机械式关闭
public void SwitchOff()
{
// 触碰式关闭
_touchableLamp.TouchOff();
}
}
4. 客户不须修改程序,直接以Adapter转换Adaptee,透过原本的Itarget界面来操作Adaptee对象。
static void Main(string[] args)
{
// 遥控器
RemoteController remoteController;
Console.WriteLine("使用同一支遥控器控制触碰式台灯:");
// 建立触碰式开关台灯
ITouchable touchableLight = new TouchableLamp();
// 透过转接器将触碰式开关台灯,转换为"假"的机械式开关台灯
ISwitchable fackSwitchableLight = new SwitchableLampAdapter(touchableLight);
// 注入遥控器欲操控之台灯实例 (符合ISwitchable界面)
remoteController = new RemoteController(fackSwitchableLight);
// 开启
remoteController.TurnOn();
// 关闭
remoteController.TurnOff();
Console.ReadKey();
}
输出结果
其他应用
在Teddy前辈的文章有提到,可以利用Adapter Pattern的特性来做为团队开发的边界连接器。试想个情境,A团队需要B团队开发的API_B来处理某种数据,此时B团队尚未将API_B开发完毕,但项目总是要继续走下去的,所以A团队就可以自行搞个API_A来延续开发活动。等待B团队API_B开发完毕后再透过Adapter Pattern来转介API_A至API_B,此例亦是Pluggable Adapter的一种应用。
参考数据
http://teddy-chen-tw.blogspot.tw/2013/05/clean-codeadapter.html
希望此篇文章可以帮助到需要的人
若内容有误或有其他建议请不吝留言给笔者喔 !
原文:大专栏 [Design Pattern] Adapter Pattern