一、 考察对象的Adapter模式
从上文看到,经过引入Adapter模式,原有的结构得到了改进。但我们还需要从客户的角度分析程序,使结构更加地合理。(这里,我们仅限于考察对象的Adapter模式。类的Adapter模式不存在下述问题。这也印证了一个事实,就是:对象的Adapter模式和类的Adapter模式各有优势,也各有缺点,设计时应根据实际情况考察。)
1、扩展的功能是否合理?
假设用户希望调用VedioMedia同时具有Play()和Resize()功能。从前面的描述来看,客户只需要实例化VedioAdapter类对象,就可以调用了。看来结构是正确的。
2、类型的扩展是否合理?
从目前的需求来看,要调用RM和MPEG类型的对象,没有任何问题。但是正如吕震宇所说,在VedioMedia类的Resize()方法中有一股腐化的味道。坏味道的根源就是if 条件语句。如果要增加新的视频类型,就需要修改Resize()方法了。这是一个设计的权衡。其实这个味道虽然够坏,但好处是简单,也不用更多的对象;但耦合性比较差。
如果我们的目标是希望更好的架构以支持耦合的松散,目前的结构就需要微调了。调整后的类图如下:

这样需要改变VedioAdapter类的代码:
public abstract class VedioAdapter:IVedioScreen
{
protected vedioMedia _vedio;
public VedioAdapter(vedioMedia vedio)
{
_vedio = vedio;
}
public void Play()
{
_vedio.Play();
}
public abstract void Resize();
}
然后实现RMAdapter和MPEGAdatper:
public class RMAdapter:VedioAdapter
{
public RMAdapter(VedioMedia vedio):base(vedio){}
public override void Resize()
{
MessageBox.Show("Change the RM screen's size.");
}
}
(MPEGAdapter的代码省略)
这样一改,要扩展就容易了,不过比之以前设计要复杂些,希望不会有人说我过度设计。如果要考虑正确性的话,RMAdapter的构造函数还需要考虑异常情况。由于构造函数的参数为VedioMedia类型,因此,客户在调用时可能会传入MPEG类型,此时RMAdapter类型的Play()行为就会发生改变。这也是和Decorator最大的不同,就是我们必须限制对象的Play()方法不能做任何改变。
public RMAdapter(VedioMedia vedio):base(vedio)
{
if (!(vedio is RM))
throw new Exception("VedioMedia object is not correct!");
}
3、 是否与原有客户系统兼容?
如果在原有的客户系统中提供了如下的类及方法:
public class MediaFile
{
public static void Play(IMedia media)
{
media.Play();
}
}
那么客户如下的调用是没有任何问题的:
MediaFile.Play(new RM());
然而,当客户要使用新的Adapter对象呢?例如:
MediaFile.Play(new RMAdapter());
显然是有问题了,因为RMAdpater类没有实现IMedia接口,且RMAdpater类的Play()方法和IMedia接口的Play()方法在性质上也是有区别的。此时,采用原有的设计就不正确了。改进的方法很简单,就是让VedioAdapter类实现IMedia接口就可以了:

public abstract class VedioAdapter:IVedioScreen,IMedia
{
……
}
根据吕震宇所说,当VedioAdapter类实现IMedia接口时,言外之意就是该Adapter也适合AudioMedia类型了。是否如此呢?可以说是一半对,一半不对。对的原因,是由于AudioMedia类也实现了IMedia接口;但别忘了,我们适配的并非类,而是对象,也就是在 VedioAdapter中传递进来的VedioMedia对象。(如果我们将传递进来的对象扩展为IMedia,那就糟糕了。震宇兄的结论就完全成立了。)同时,我们在构造函数的异常处理,也保证了AudioMedia类型对于VedioAdapter是非法的。
写到这里,我觉得本文已经超出了原来的设想,有些研究的味道了。嗯,还算不错。那么就继续研究下去吧。
二、 引入Decorator模式
按照最初的需求,我引入Decorator模式试一试。最初的需求是,需要为RM和MPEG类在不改变原有代码的情况下,添加Resize()方法,而其原来的Play()方法不变。调整设计类图:

上图的橙红色区域为Decorator模式的主体,至于IVedioScreen接口,仅仅是为VedioDecorator增加Resize()方法而引入的,其本身与Decorator无关,除非我要实现的Decorator功能,通过该接口来实现。代码如下:
public abstract class VedioDecorator:VedioMedia,IVedioScreen
{
private VedioMedia _vedio;
public VedioAdapter(vedioMedia vedio)
{
_vedio = vedio;
}
public VedioMedia Vedio
{
get {return _vedio;}
}
public abstract void Resize();
}
注意看,与前面Adapter模式的VedioAdapter类比较,除增加了对VedioMedia的派生外,还减少了Play(),因为该方法已经从VedioMedia类中派生获得。这样的话,RMDecorator和MPEGDecorator,也需要做相应的改变:
public class RMDecorator:VedioDecorator
{
public RMDecorator (VedioMedia vedio):base(vedio)
{
if (!(vedio is RM))
throw new Exception("VedioMedia object is not correct!");
}
public override void Play()
{
Vedio.Play();
}
public override void Resize()
{
MessageBox.Show("Change the RM screen's size.");
}
}
到这个时候,我忽然觉得引入Decorator模式,已经有些力不从心了。为什么呢?最大的障碍就是我们的需求不能更改VedioMedia类型 Play()方法的既有行为。这个时候,所谓的Decorator已经失去了原来的意义。其实我觉得,此时的设计,应该是结合了Adapter模式与 Decorator模式而衍生出的新的结构。
那么,我为什么还要在本文提出引入Decorator模式呢。这来源我对于设计模式一向的观点:不要为了模式而模式!GOF的23种模式,并非茴香豆的“茴”字,我也并非孔乙己,要你回答“茴”字的写法,却忽略了使用设计模式的真正精神。设计模式归根结底是拿来用的。只要符合你的要求,各种模式随你怎么变都可以。因此,不管是前文所述的Adapter模式,还是改进后的Adapter模式,或者引入的Decorator模式,其中的变化是灵活的,选择权最终还是你。
三、 正宗的Decorator模式
不过,我还是很有兴趣继续探讨下去,仍然借助媒体播放这个例子,来谈一谈Decorator模式的一般应用。现在我们要求RM和MPEG媒体在播放前,首先要显示媒体文件的版权信息。请注意,这个需求,并非是为RM等媒体增加ShowCopyright()方法,而Play()方法保持不变。恰恰相反,新的需求装饰了Play()的行为,它要求Play()的同时能够支持ShowCopyright的功能。类图如下:

在这里,VedioDecorator是装饰类的抽象类,而CopyRightVedioDecorator类则具体装饰了Play()的功能。
public abstract class VedioDecorator:VedioMedia
{
private VedioMedia _vedio;
public VedioAdapter(vedioMedia vedio)
{
_vedio = vedio;
}
public VedioMedia Vedio
{
get {return _vedio;}
}
}
然后实现CopyRightVedioDecorator类,为Play()方法装饰显示版权信息的功能:
public class CopyRightVedioDecorator:VedioDecorator
{
private CopyRight _copyRightMark;
public CopyRight CopyRightMark
{
get {return _copyRightMark;}
set {_copyRightMark = value;}
}
public override void Play()
{
_copyRightMark.ShowCopyRight();
Vedio.Play();
}
}
我们还可以继续装饰VedioMedia的Play行为,例如,要求在播放媒体文件之前,必须放一段广告,那么我们可以继续提供一个AdvertisementVedioDecorator装饰类。道理与上一样,不再赘述。
通过本例,我们可以看到Decorator模式与对象的Adapter模式的区别。
实现的区别:
1、 Decorator抽象类应继承要装饰的类,同时又聚合该类的实例对象;而对象的Adapter模式则只聚合,不继承;
2、 Decor模式并没有引入新的接口,除非要装饰的行为需要使用该接口;而对象的Adapter模式则引入了新的接口,以此来装配原有的对象,使其具有了新接口的方法;
因此,适用的场景也就有所不同:
1、 Decorator模式如其名,一般并不提供新的行为,而是在原有的行为上进行补充,即装饰的含义。
2、 Adapter模式则是为对象引入新的行为,使其匹配新的接口,即为适配的意义所在。
分享到:
相关推荐
《让僵冷的翅膀飞起来》系列之三——从Adapter模式到Decorator模式 - Wayfarer's Prattle - 博客园
内容概要:本文深入剖析了安川MP7系列工业控制系统的关键源码,重点介绍了运动轨迹规划、通信协议处理以及故障处理机制等方面的技术细节。通过对实际代码片段的解读,揭示了该系统在硬件寄存器直接访问、特殊功能码处理等方面的独特之处。同时,文中还分享了一些基于实践经验得出的重要参数设置及其背后的故事,如特定摩擦补偿系数的选择原因等。 适合人群:从事工业自动化领域的工程师和技术人员,尤其是对安川产品有一定了解并希望深入了解其内部工作机制的专业人士。 使用场景及目标:帮助读者掌握安川MP7系列控制器的工作原理,提高对类似系统的维护能力和故障排查效率。对于想要进一步研究或二次开发该系统的开发者来说,也能提供宝贵的参考资料。 其他说明:文章不仅限于理论讲解,还包括了许多来自一线的实际案例和经验教训,使读者能够更好地理解和应用所学知识。
自动化测试与脚本开发_Python3_pynput_键盘鼠标操作录制执行代码生成工具_用于自动化测试_脚本录制_重复操作模拟_宏命令生成_提高工作效率_支持GUI界面_跨平台兼容_
嵌入式八股文面试题库资料知识宝典-深入分析Windows和Linux动态库应用异同.zip
嵌入式八股文面试题库资料知识宝典-C语言总结.zip
内容概要:本文详细探讨了风储直流微电网中母线电压控制的关键技术。首先介绍了风储直流微电网的背景和发展现状,强调了母线电压控制的重要性。接着阐述了永磁风机储能并网技术,解释了永磁风机如何通过直接驱动发电机将风能转化为电能,并确保与电网的同步性和稳定性。然后深入讨论了双闭环控制MPPT技术,这是一种通过内外两个闭环控制系统来实现实时调整发电机运行参数的技术,确保风机始终处于最大功率点附近。最后,文章探讨了储能控制母线电压平衡的方法,即通过储能系统的充放电操作来维持母线电压的稳定。结论部分指出,通过这些技术的有机结合,可以实现对风储直流微电网的有效管理和优化控制。 适合人群:从事新能源技术研发的专业人士、电气工程研究人员、风电系统工程师。 使用场景及目标:适用于希望深入了解风储直流微电网母线电压控制策略的研究人员和技术人员,旨在帮助他们掌握最新的控制技术和方法,以提高系统的稳定性和效率。 其他说明:文章还对未来风储直流微电网的发展进行了展望,指出了智能化和自动化的趋势,以及储能技术的进步对系统性能的影响。
嵌入式八股文面试题库资料知识宝典-C++object-oriented.zip
内容概要:文章详细介绍了HarmonyOS的目录结构及其重要性,从整体框架到核心目录的具体功能进行了全面剖析。HarmonyOS凭借其分布式架构和跨设备协同能力迅速崛起,成为全球操作系统领域的重要力量。文章首先概述了HarmonyOS的背景和发展现状,强调了目录结构对开发的重要性。接着,具体介绍了根目录文件、AppScope、entry和oh_modules等核心目录的功能和作用。例如,AppScope作为全局资源配置中心,存放应用级的配置文件和公共资源;entry目录是应用的核心入口,负责源代码和界面开发。此外,文章还对比了HarmonyOS与Android、iOS目录结构的异同,突出了HarmonyOS的独特优势。最后,通过旅游应用和电商应用的实际案例,展示了HarmonyOS目录结构在资源管理和代码组织方面的应用效果。; 适合人群:具备一定编程基础,尤其是对移动操作系统开发感兴趣的开发者,包括初学者和有一定经验的研发人员。; 使用场景及目标:①帮助开发者快速理解HarmonyOS的目录结构,提高开发效率;②为跨设备应用开发提供理论和技术支持;③通过实际案例学习资源管理和代码组织的最佳实践。; 其他说明:HarmonyOS的目录结构设计简洁明了,模块职责划分明确,有助于开发者更好地管理和组织代码和资源。随着万物互联时代的到来,HarmonyOS有望在开发便利性和生态建设方面取得更大进展,吸引更多开发者加入其生态系统。
内容概要:本文详细介绍了飞轮储能充放电控制的Simulink仿真模型,重点在于采用永磁同步电机的矢量控制和dq轴解耦控制策略。充电时,外环控制转速,内环控制dq轴电流;放电时,外环控制直流母线电压,内环同样控制dq轴电流。文中还讨论了硬件与软件环境的选择,以及仿真模型的调试与运行情况,最终得出该模型具有良好的跟随性能和波形完美度。 适用人群:从事电力电子系统、储能技术和Simulink仿真的研究人员和技术人员。 使用场景及目标:适用于需要对飞轮储能系统进行深入研究和仿真的场合,旨在提高充放电效率和稳定性,满足不同应用场景的需求。 其他说明:该仿真模型已调试完成,可以直接用于进一步的研究和实际应用,为未来的飞轮储能技术研发提供了有价值的参考。
嵌入式八股文面试题库资料知识宝典-北京瑞德方科技.zip
嵌入式八股文面试题库资料知识宝典-同方万维硬件测试工程师.zip
1_15套python PDF格式.zip
内容概要:本文详细介绍了三相三电平整流器的仿真过程及其性能分析。文中首先概述了三相三电平整流器的基本概念及其在电力系统中的重要作用,接着重点探讨了电压电流双闭环控制方式的工作原理和优势,以及SPWM调制技术的具体应用。通过仿真文件展示了整流器在不同条件下的响应情况,验证了这两种技术的有效性和优越性。最后,作者表达了对未来实际应用的期望。 适合人群:从事电力电子研究的技术人员、高校相关专业师生、对电力控制系统感兴趣的工程爱好者。 使用场景及目标:适用于希望深入了解三相三电平整流器工作原理和技术细节的研究人员;目标是在理论基础上掌握电压电流双闭环控制和SPWM调制的实际应用方法。 其他说明:本文提供的仅为仿真文件,未涉及实物实验数据。
嵌入式八股文面试题库资料知识宝典-恒光科技.zip
嵌入式八股文面试题库资料知识宝典-北京天华威视科技有限公司面试题.zip
嵌入式八股文面试题库资料知识宝典-微软研究院笔试题目的答案.zip
Arduino UART实验例程,开发板:正点原子EPS32S3,本人主页有详细实验说明可供参考。
嵌入式八股文面试题库资料知识宝典-朝歌数码.zip
嵌入式八股文面试题库资料知识宝典-Cortex系列.zip
嵌入式八股文面试题库资料知识宝典-中科中科长青笔试题.zip