《让僵冷的翅膀飞起来》系列之三——从Adapter模式到Decorator模式

《让僵冷的翅膀飞起来》系列之一——从实例谈OOP、工厂模式和重构

《让僵冷的翅膀飞起来》系列之二——从实例谈Adapter模式

一、 考察对象的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模式则是为对象引入新的行为,使其匹配新的接口,即为适配的意义所在。

posted on 2005-01-13 15:30 张逸 阅读(4085) 评论(30)  编辑 收藏 网摘 所属分类: Design & Pattern

评论

#1楼  2005-01-13 18:31 柳三公子      

按照我的理解,Decorator模式的抽象类必须继承要装饰的类的最主要原因,是可以对一个功能反复添加不同的装饰,提供无穷多的功能。一个已经被装饰的类,可以把自己本身作为参数,传入另一个装饰类中。
  回复  引用  查看    

#2楼 [楼主] 2005-01-13 19:31 wayfarer      

应该说是继承和聚合两者加起来,才达到装饰的作用。   回复  引用  查看    

#3楼 [楼主] 2005-01-13 19:34 wayfarer      

Vistor模式,我还在思考。既然已经改为了所谓系列,自然就是不准备马上结束了。
  回复  引用  查看    

#4楼  2005-01-13 19:58 idior      

wayfarer帮我把上面的话删除,我被“上图的橙红色区域为Decorator模式的主体”这句话误导了,郁闷 :(   回复  引用  查看    

#5楼  2005-01-13 20:15 idior      

实在不好意思,现在看来似乎没有什么问题,好文,顶一下   回复  引用  查看    

#6楼  2005-01-13 20:21 idior      

我来补充一下大家可以发现proxy模式和他们也很想像,它和decrator模式的区别在于它聚合的是一个具体类,而非抽象类,Decorator模式正如wayfarer所说是用于修饰原有类的,是对原来行为的修改而非添加新的行为的模式。
proxy和它类似,也是修饰原来的行为(谁来谈谈他们的区别?)

而Adapter模式则是为对象引入新的行为,使其匹配新的接口,即为适配的意义所在。

其实很多情况下,Adaptor是用来对老的或者别人的系统进行改名用的,不是引入新的行为,而是将老的行为改个名字,并适应新的系统(实现重用的目的)。(比如以前叫 BoFang()现在改为Play()功能并不一定变 )
 
因为Adaptor往往是将老的系统的代码重用到新的系统,所以老的功能就可以不用管它了,所以对象的Adapter模式只聚合,不继承。而我们前面要求Adaptor去适应IMedia接口已经有点苛求Adaptor了,因为Adaptor如我前面所说更多的不是用于此种扩展功能的目的,所以这个添加新功能的方法最终将交到Visitor模式的手上 :)
  回复  引用  查看    

#7楼  2005-01-13 21:18 吕震宇      

呵呵,我又来捧场了!不错,希望能够继续这个系列的文章。不过还是有几个问题:

1、public abstract class VedioDecorator:VedioMedia,IVedioScreen
{
private VedioMedia _vedio;

这里是否可以考虑使用protected VedioMedia _vedio;?

2、我不完全赞同“Decorator模式如其名,一般并不提供新的行为,而是在原有的行为上进行补充,即装饰的含义”。其实如果提供的新行为与原有旧内容没有什么瓜葛的化,是可以应用Decorator模式的。就像在我的设计模式教案中的BorrowableBook一样。

3、我认为引入Decorator模式后的例子似乎还存在问题:
public RMDecorator (VedioMedia vedio):base(vedio)
{
if (!(vedio is RM))
throw new Exception("VedioMedia object is not correct!");
}

其中if (!(vedio is RM))就是最大的问题。Visitor模式似乎是个不错的选择,还有工厂模式或Builder模式什么的,现在我也在思考,不想让这些模式影响了分析具体问题。等有了思路后再来回复。   回复  引用  查看    

#8楼  2005-01-13 21:44 吕震宇      

现在看来,好像“类的Adapter模式”成了最好的选择了。 :)   回复  引用  查看    

#9楼  2005-01-13 21:55 idior      

@吕兄
Decorator模式如其名,一般并不提供新的行为,而是在原有的行为上进行补充,即装饰的含义。

参考柳三公子的话
按照我的理解,Decorator模式的抽象类必须继承要装饰的类的最主要原因,是可以对一个功能反复添加不同的装饰,提供无穷多的功能。一个已经被装饰的类,可以把自己本身作为参数,传入另一个装饰类中。

如果你添加了新的功能,由于新功能不属于原来的接口,那么这个添加新功能的Decorator是无法被反复的修饰的。就是说它只能单独使用,无法参与到叠加的功能中。

当然,Decorator也可以象你那样使用。还有就是当ConcreateComponent有多个的时候很容易出现if (!(vedio is RM))
这种问题   回复  引用  查看    

#10楼 [楼主] 2005-01-13 22:02 wayfarer      

是啊。从最初的需求来看,还是类的Adapter模式更清晰,简单。

“public abstract class VedioDecorator:VedioMedia,IVedioScreen
{
private VedioMedia _vedio;

这里是否可以考虑使用protected VedioMedia _vedio;? ”

因为我在抽象的Decorator类中,将VedioMedia类对象_vedio,定义为public属性了,且子类的构造函数,调用了基类的。所以,可以不定义为protected。

使用if (!(vedio is RM))是无奈之举。Vistor模式,我在思考。反正尽量写系列出来吧。   回复  引用  查看    

#11楼  2005-01-14 10:56 KingofSC [未注册用户]

我一直对adapter,proxy,decorator模式没有很好的区分清楚
因为以一个最直接简单的应用上述三种模式的例子来说
都是一个
OriginalClass -(adapt,proxy,decorate)> TargetClass
的过程
其中实现方法有继承,聚合
但其作用都是为了给客户一个新的TargetClass而对OriginalClass进行相应的重构(或者这里用重构不合适,我指的重构是最终目的,也就是OriginalClass不适合客户需求了,要重构一个TargetClass出来)
所以我老是决定三种模式非常相似

而楼主说的:
通过本例,我们可以看到Decorator模式与对象的Adapter模式的区别。
实现的区别:
1、 Decorator抽象类应继承要装饰的类,同时又聚合该类的实例对象;而对象的Adapter模式则只聚合,不继承;
2、 Decor模式并没有引入新的接口,除非要装饰的行为需要使用该接口;而对象的Adapter模式则引入了新的接口,以此来装配原有的对象,使其具有了新接口的方法;

因此,适用的场景也就有所不同:
1、 Decorator模式如其名,一般并不提供新的行为,而是在原有的行为上进行补充,即装饰的含义。
2、 Adapter模式则是为对象引入新的行为,使其匹配新的接口,即为适配的意义所在。

这些比较对我认识模式有很大帮助
能否就此比较一下三个模式?
  回复  引用    

#12楼  2005-01-14 11:00 KingofSC [未注册用户]

甚至乎现在我觉得facade模式也是跟这三种很像,只不过facade的originalClass比较多而已 :(   回复  引用    

#13楼  2005-01-14 14:25 柳三公子      

to wayfarer
应该说是继承和聚合两者加起来,才达到装饰的作用。
=================================
我不同意你的话,从装饰的本意来讲,最大的好处是可以一层一层的往上装饰,为一个功能添加无穷多的附加功能,如果通过你说的聚合与继承相结合的话,在嵌套装饰的时候,就无法把Decorator对象本身作为下一次装饰的目标。

当然也有解决方法,就是通过一个方法或属性来返回聚合的对象
CopyRightVedioDecorator.VedioAdapter(video);
AdvertisementVedioDecorator.VideoAdapter(CopyRightVedioDecorator.GetVideoObj());
这样的设计……丑陋了点,呵呵。

本来仅通过继承的话,是可以写成如下这样的:(这里假设VideoMedia有拷贝构造函数)
VideoMedia m = new CopyRightVedioDecorator(new AdvertisementVedioDecorator(new VideoMedia));
因为不论是CopyRightVedioDecorator还是AdvertisementVedioDecorator本身都是VideoMedia的子类,可以作为拷贝构造函数的参数来传递,即使要添加更多的Decorator,也只是继续扩展这句语句而已。

to idior
其实很多情况下,Adaptor是用来对老的或者别人的系统进行改名用的,不是引入新的行为,而是将老的行为改个名字,并适应新的系统(实现重用的目的)。(比如以前叫 BoFang()现在改为Play()功能并不一定变)
===========================================================================
我记得纯粹改名字的模式好像是叫wapper(封装,可能拼错)不知道这个封装和Adaptor是不是指的同一种模式,但我觉得总是有区别的,封装模式是要把被封装的类全部换名实现,使其支持另一套接口。而Adaptor是为类添加1..*个功能,而不是改名。
  回复  引用  查看    

#14楼 [楼主] 2005-01-14 19:30 wayfarer      

@柳三公子:
不然。

就以你的例子来说吧。
VideoMedia m = new CopyRightVedioDecorator(new AdvertisementVedioDecorator(new VideoMedia));

这里,你在构造函数里传递的实例即是Decoratee,也就是要装饰的对象。new VedioMedia()和new AdvertisementVedioDecorator()都是。

那么你为什么要在构造函数里传递这个参数呢?因为我们在构造函数里是这样写的(对于抽象装饰类):
public VedioDecorator(VedioMedia vedio)
{
this._vedio = vedio;
}

那么这个_vedio又是什么呢?在抽象装饰类中,我们应定义一个保护字段:protected VedioMedia _vedio;
或者定义一个公共属性public VedioMedia Vedio。
有了这个字段或属性,才能接受你在构造函数里传递进来的被装饰的对象啊。

如play方法:
public override void Play()
{
//这里是装饰的内容;
Vedio.Play();//这里的Vedio就是被装饰的对象,也就是你通过构造函数传递的参数;而该对象恰好属于VedioMedia类型,又被装饰类包含,即聚合关系;
}


那么,请问这个字段或属性,不是抽象装饰类与抽象被装饰类的聚合,又是什么呢?

所以,聚合才是达到装饰的本质之所在。那么为什么又要去继承被装饰类呢?那是因为,具体装饰类本身,也能够被装饰,如你前面写的new AdvertisementVedioDecorator()。如果你不继承,这个new AdvertisementVedioDecorator()装饰类就不能作为参数传递到装饰类的构造函数里了。因为它没有继承的话,它就不是VedioMedia类型,自然转换就会失败。

你说的Adaptor功用是对的,确实如此。我会在本系列的后续文章中,继续关注Adaptor模式,为媒体播放器,引入收音机功能,此时,最能体现Adaptor模式的价值。   回复  引用  查看    

#15楼  2005-01-17 17:54 柳三公子      

@wayfarer
看了你的回复后,我又仔细复习了《设计模式》这本书,认识到自己犯了一个错误,谢谢你的指出。   回复  引用  查看    

#16楼  2005-02-05 01:28 西湖一条鱼 [未注册用户]

很佩服wayfarer的卓见和理解
柳三居然会说·认识到自己犯了一个错误·,也让人佩服。
能不能问两位一个简单的问题。
我是刚刚接触design pattern,
没有任何经验的。。。。

问题是>
VideoMedia m = new CopyRightVedioDecorator(new AdvertisementVedioDecorator(new VideoMedia));

那么此时的 m 就是一个具体的 CopzRightVedioDecorator的实例了。
那么这个时候 m.play 除了会调用自己的.ShowCopyRight();还会调用基类的Play()。

可是问什么AdvertisementVedioDecorator的方法可以被调用呢?

这个时候的m应该是一个CopzRightVedioDecorator的实例,以及VedioDecorator, VedioMedia的实例(基类)。
但是AdvertisementVedioDecorator可是和CopyRightVedioDecorator是结构上平行的。

这就是我为什么没理解为什么Decrator可以无限扩展的原因。

两位大虾如果有时间,
请不吝赐教。
在下感激不尽!   回复  引用    

#17楼  2005-02-05 01:45 西湖一条鱼 [未注册用户]

刚刚发完这个帖子我就觉得我好像想通了一点,
也就是说
public override void Play()
{
_copyRightMark.ShowCopyRight();
Vedio.Play(); //应该是 _Vedio.Play()吧?
}
而此时的_Vedio是一个AdvertisementVedioDecorator的实例.

同理,
AdvertisementVedioDecorator::play会调用
VedioMedia.play().

呵呵,
原来Decorator是这个用法啊。。。。。
汗.....

既然没什么建设性,
版主给这两个帖子删了吧,
否则将来我成牛人了,
班主说|哈哈,想当年,这家伙跟猪头一样....|就不好玩了。

不过很喜欢你的这些东西
以后常来~~~

  回复  引用    

#18楼 [楼主] 2005-02-08 17:15 wayfarer      

@西湖一条鱼:

学习就是从错误中进步的嘛,留下来还是蛮有意思的。删了就不符合博客的精神了。   回复  引用  查看    

#19楼  2005-02-23 14:22 lrabby [未注册用户]

我是个新手,想问几个问题。

在这里这个例子我认为也可以用下面的方式来解决。

--------------------------------

public class MediaFile
{
public MediaFile()
{
}

public static void PlayDecorator(VideoMedia video, CopyRight cr)
{
cr.ShowCopyRight();
video.Play();
}
}

----------------------------------

上面的这个例子并不需要继承 VedioMedia 。 直接引用 VedioMedia 和 CopyRight 就可以了。

这样实现比 用 Decorator 来实现有什么缺点吗?   回复  引用    

#20楼 [楼主] 2005-02-24 18:03 wayfarer      

@lrabby:

你的实现只能针对Play的同时显示版权信息的需求。如果,我更改了需求,例如不显示版权信息了,或者,显示其他的内容。这个实现就有问题了。装饰模式的特点就是能够灵活装饰目的对象,以此来达到应付变化的目的。   回复  引用  查看    

#21楼  2005-02-26 00:04 lrabby [未注册用户]

@wayfarer

---------------------
装饰模式的特点就是能够灵活装饰目的对象,以此来达到应付变化的目的。
---------------------

到底怎么个灵活法,我还是有点不太清楚。我在自己琢磨琢磨。
如果你又时间的话,不妨再详细给我说说。谢谢了。
  回复  引用    

#22楼 [楼主] 2005-02-26 13:42 wayfarer      

@lrabby:
关于装饰模式,你可以参考吕震宇的设计模式系列。至于说为什么灵活,我可以举一个例子。如:
被装饰对象
public class Target
{
public void Print()
{
Console.WriteLine("The Target!");
}
}
现在假设要为Target作装饰,要求在输出The Target之前能输出A、B、C或三者间的组合。如打印出如下的三行(组合一):
A
C
The Target!
或打印(组合二):
C
B
The Target!
等等的输出形式。如果你不使用装饰模式,必然要为这些输出编写不同的Print()方法,才能满足要求。
现在使用装饰模式:
抽象装饰类:
public abstract class Decorator:Target
{
public Decorator(Target target)
{
_target = target;
}
public abstract void Print();
protected Target _target;
}
具体装饰类:
DecoratorA负责打印A字符:
Public class DecoratorA:Decorator
{
public DecoratorA(Target target):base(target){}
public void Print()
{
Console.WriteLine("A"):
_target.Print();
}
}
DecoratorB和DecoratorC与DecoratorA类似。
再看看客户端调用方式:
public class Client
{
public static void Main()
{
//只打印一行The Target;
Target target = new Target();
target.Print();

//打印组合一:
//注意实例化的顺序,与输入相反,应从内到外;
//想象成一种层层的包裹关系,最外层为A,所以最后实例化;
DecoratorC c1 = new DecoratorC(target);
DecoratorA a1 = new DecoratorA(c1);
a1.Print();

//打印组合二:
DecoratorB b2 = new DecoratorB(target);
DecoratorC c2 = new DecoratorC(b2);
c2.Print();
}
}

以上代码我是随便写的,不过你可以把它们复制过去,编译运行一下,这样能促进你的理解。同时你可以在Clint类中,实现打印其他组合的代码。

相信这样的解释能帮助你理解。   回复  引用  查看    

#23楼  2005-04-15 12:15 iamtheprogram [未注册用户]

设计模式的缺点就是会增加很多类,而且如果设计得不好,将会使原本很简单的功能变得晦涩难懂,阅读代码的人首先要理解类之间的关系,如果设计的人对类取的名称不好或者本身的设计就很多余,那就是一种悲哀,原本想减少工作量变成增加工作量。
其实做软件的目的就是为客户服务,最重要的是简单稳定,对于设计模式的选择要慎重。先解决有和无的问题。   回复  引用    

#24楼  2005-04-15 12:29 iamtheprogram [未注册用户]

如果只是想在Play的时候显示版本信息ShowCopyRight,能否这样:

新建一个类VedioMedia2
class VedioMedia2 : IMedia
{
VedioMedia _vm;
VedioMedia2(VedioMedia vm)
{
_vm = vm;
}

void Play()
{
ShowCopyRight();
_vm.Play();
}

void ShowCopyRight();
}

不知道这样属于什么模式?   回复  引用    

#25楼  2005-04-15 12:43 iamtheprogram [未注册用户]

其实Decorator的存在就是为了装饰类里面的方法。   回复  引用    

#26楼  2005-04-30 13:00 d [未注册用户]

ew   回复  引用    

#27楼  2006-08-24 18:06 怪怪 [未注册用户]

你的那个适配器,感觉上和作用上更像一个Proxy,体现Adpater的价值不够。另外lrabby的,完全可以定义一个接口传进去,照样可以换操作,其实Decorator装饰的主要还是方法,如果传一个delegate进去,delegate的参数又能够传一个delegate进去,应该可以不用传统的Decorator实现decorator模式。

另外吕老师那边讨论的问题其实我想可以探讨的地方是,你的Decrator抽象类可能放错了位置才导致这种结果。不知道是否过于注重面向高层次的接口了,而忽略了具体对象的差异性。譬如应用Decorator后play的video is rm问题,如果你的VideoDecorator分别在RM和MPEG这些比较具体的层次下面各有一个就绝无问题,但这样就把问题具体化到具体的对象了,构建一组围绕RM的处理,一组围绕MPEG的处理,我想考虑到差异性,也绝非不合理。至于resize这类本身不会有问题的话,如果为了消除重复代码,从RMDecorator/MPEGDecorator等下面的每个具体的Decorator抽象出去RM/MPEG共用一组操作就可以了。这样看着一下出来两个Decorator,甚至以后有不同类型了有不同的Decorator,但实际上模型在现实上本身就是如此,也并非一定要抽象到多大的高度才是最优设计,毕竟这样你仍然阻止了类的爆炸。   回复  引用    

#28楼  2006-08-24 18:07 怪怪 [未注册用户]

发现大家的文章都很早,要拍马追都赶不上了...   回复  引用    

#29楼  2007-02-01 13:43 蚊子飞飞 [未注册用户]

我更晚,刚接触设计模式,感触颇多,旧人已不在,新人忘断肠.....   回复  引用    

#30楼  2007-02-13 16:05 killer [未注册用户]

我也刚接触,看了模式设计后,感觉云里雾里,很容易混淆。

还是那句话 “旧人已不在,新人忘断肠..... ”   回复  引用    





标题  
姓名  
主页
Email (博主才能看到) 
验证码 *  看不清,换一张 [登录][注册]
内容(请不要发表任何与政治相关的内容)  
  登录  使用高级评论  新用户注册  返回页首  恢复上次提交      
该文被作者在 2006-04-07 09:18 编辑过
Google站内搜索

相关文章:

相关链接:
 

导航

公告

logo.gif
我的著作与译作

《软件设计精要与模式》

《WCF服务编程》

MVP_Horizontal_BlueOnly.png

From 03-03-2006
Counter: site stats

与我联系

搜索

 

常用链接

我参加的小组

我参与的团队

随笔分类(245)

随笔档案(238)

最新随笔

积分与排名

最新评论

阅读排行榜

评论排行榜