设计模式--装饰模式

现在我们来学习装饰模式。说实话,真不想写这个,因为提到这个装饰,程序员就很伤感(我也是),就想到了遥远地她和虚无缥缈地房子。房子都还没着落,谈什么装修和粉饰啊。一堵粗糙的墙,刷上白白地粉,再贴上几张壁画,整个一焕然一新。多美的事啊。哎,既然想到了,就咬着牙多想会,至少心里还有个期盼。真心祝愿大家看完这篇文章后都能够梦想成真。

生活中的装饰是很好理解的,我们打两个比方。先还是说房子的装修(我不是故意的),装修无非就是要在墙上刷上粉贴上壁纸挂上饰物,让房屋显得更加雅致美观。但墙还是那堵墙,本质不会改变,只是多了一层包装而已。再看一个例子,假设说你有一个一个的列车车厢,每一个车厢都有对运输功能作一些不同的增强,然后你选取一些这样的车厢,连接起来,形成一个专列,这个专列的功能就是组成它的那些车厢的功能的叠加。这些都是生活中比较典型的装饰模式。

看了《大话设计模式》后,凭自己的理解写了一个90坦克的小程序,当然不是玩的,紧为了表现出这种设计模式的好处,所以用控制台应用程序。

大鸟:“如果让你写一个90坦克的程序,坦克能吃各种宝贝起到不同的作用,现在就拿3中宝贝,帽子(受到攻击无影响)、枪(双弹、并能射穿钢板,而且能抵一命)、船(能在水里行走,也能抵一命),说说你的思路?”

“这还不简单,写一个接口,里面封装一些属性和方法,然后写不同的子类,在子类中实现接口不就行了!”小鸟得意地回答,心想这样可把面向对象编程用进去了啊,还用到多态,多灵活啊。

“呵呵,小菜就是小菜啊,表面上用到了多态接口,你有没有算下,你要写多少个子类?我和你算一下,什么都没吃的最原始坦克、吃帽子的坦克、吃枪的坦克、吃船的坦克、同时吃了帽子和枪的坦克、同时吃了帽子和船的坦克、同时吃了枪和船的坦克、同时吃帽子船和枪的坦克,一共2的3次方种,也就是8种组合呢,这才3种宝贝呢,真正的游戏里面那么多种,共2的n次种呢”大鸟道。

“有什么好办法呢?”

“不懂就学,其实也没什么稀罕的,这可以用一个非常有意思的设计模式来实现。”

装饰模式

装饰模式,动态地给一个对象添加一些额外的职责,就增加功能来说,装饰模式比生成子类更为灵活。

记住,面向对象编程其实就是模拟现实,当你遇到一个问题苦思冥想得不出答案的时候,不防走出屋子,灵感或许来自于现实。

现实生活中,U盘、MP3播放器、手机等具有USB接口的电子产品一般具有USB最基本的功能,那就是数据传输----写数据和读数据,因此,抽象出一个接口:

/// <summary> /// USB接口 /// </summary> public interface IUSB { /// <summary> /// 读数据 /// </summary> void ReadData(); /// <summary> /// 写数据 /// </summary> void WriteData(); }


分别让U盘和MP3播放器来实现数据传输功能:

/// <summary> /// U盘 /// </summary> public class UDisk:IUSB { #region IUSB 成员 public void ReadData() { Console.WriteLine("U盘读数据....."); } public void WriteData() { Console.WriteLine("U盘写数据...."); } #endregion } public class MP3Player:IUSB { #region IUSB 成员 public void ReadData() { Console.WriteLine("Mp3播放器读数据....."); } public void WriteData() { Console.WriteLine("Mp3播放器写数据...."); } #endregion } /// <summary> /// 装饰者 /// </summary> public class Decorator:IUSB { private IUSB usb; public Decorator(IUSB usb) { this.usb = usb; } #region IUSB 成员 public virtual void ReadData() //其实执行的是IUSB的ReadData { usb.ReadData(); } public virtual void WriteData() //其实执行的是IUSB的WriteData { usb.WriteData(); } #endregion }


这里装饰者Decorator与IUSB之间的关系满足is–a关系,即Decoratoris-aIUSB,即子类一定是父类,而父类不一定是子类。这里虽然是接口,但在这儿一样适用,话句话说,人一定是动物,而是动物就不一定是人了。

装饰者Decorator同时还与IUSB直接满足has–a的关系。两个类之间要进行通信,常在一个类中引用另一个类,这个在WinForm种的窗体之间通信用的多,一个窗体如果要访问另外一个窗体的public属性的属性或方法,常引用要访问的窗体作为私有成员变量。这是UML种的关联关系。

public class People //人类 { private Climate climate; //气候 }


人类和气候不是彼此独立的,气候的变化会影响到人类不同的属性或行为,因此在人类中引用气候。

进一步研究发现,在Decorator的构造函数执行后,同时也确定了接口,也就是它们同时生成,满足合成关系。

class Bird { private Wing wing; public Bird() { wing = new Wing();//在鸟Bird类中,初始化时,实例化翅膀Wing,它们之间同时生成 } }


上面的USB接口例子,例如MP3播放器除了读数据和传输数据的基本功能外,还有播放音乐的功能、阅读电子书、照相等功能。你可以更改类,添加新的方法或字段属性来达到目的,但这样违背了开闭原则,况且有的Mp3播放器没有照相功能。装饰模式是为已有功能动态地添加更多功能的一种方式。

/// <summary> /// 音乐播放器,提供播放音乐的功能 /// </summary> public class MusicPlayer:Decorator { public MusicPlayer(IUSB usb) : base(usb) { } /// <summary> /// 播放音乐,新增的功能 /// </summary> public void PlayMusic() { Console.WriteLine("播放音乐....."); } } /// <summary> /// 照相机 /// </summary> public class Camera:Decorator { public Camera(IUSB usb) : base(usb) { } /// <summary> /// 照相,新增的功能 /// </summary> public void TakePhotoes() { Console.WriteLine("照相....."); } } 客户端代码: class Program { static void Main(string[] args) { IUSB mp3player = new MP3Player(); //只能读写数据的mp3 MusicPlayer player= new MusicPlayer(mp3player); //装饰音乐播放 player.ReadData(); player.WriteData(); player.PlayMusic(); //具有播放音乐的功能 Camera mp3Carm = new Camera(player); mp3Carm.TakePhotoes(); //具有照相的功能 Console.ReadKey(); } }


经过两次“装饰后”,原本只能读写数据的播放器现在能播放音乐和照相了,这都是在客户端动态添加的功能。共2^2=4种组合,只能传输数据、能传输数据能播放音乐、能传输数据能照相、能传输数据能播放音乐也能照相。以下是组合,或许没有只能传输数据的MP3吧,就当是坏了的吧,当U盘用O(∩_∩)O~。

①只能读写数据的MP3:IUSBmp3player=newMP3Player();//只能读写数据的mp3

②能传输数据并能播放音乐的Mp3组合:

IUSBmp3player=newMP3Player();//只能读写数据的mp3

MusicPlayerplayer=newMusicPlayer(mp3player);//装饰音乐播放

装饰后的player即是。

③能传输数据能照相的MP3组合:

IUSBmp3player=newMP3Player();//只能读写数据的mp3

Cameramp3=newCamera(mp3player);//新增照相功能

④能传输数据也能播放音乐和照相的Mp3组合

“小鸟,看了这个例子后刚才的90坦克小游戏能设计了吧?”大鸟问道。

“没问题。”小鸟很自信地回答。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值