设计模式--装饰模式

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

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

看了《大话设计模式》后,凭自己的理解写了一个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关系,即Decorator is-a IUSB,即子类一定是父类,而父类不一定是子类。这里虽然是接口,但在这儿一样适用,话句话说,人一定是动物,而是动物就不一定是人了。

装饰者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: IUSB mp3player = new MP3Player();  //只能读写数据的mp3

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

IUSB mp3player = new MP3Player();  //只能读写数据的mp3

            MusicPlayer player= new MusicPlayer(mp3player); //装饰音乐播放

装饰后的player即是。

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

IUSB mp3player = new MP3Player();  //只能读写数据的mp3

            Camera mp3 = new Camera(mp3player);  //新增照相功能

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

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

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

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值