• 装饰器模式


    介绍:

          在软件系统中,我们有时候需要对对象的功能进行扩展,继承虽然能够解决此类问题,但是由于继承本身的一些缺点使得扩展不能动态的进行,并且面对功能扩展间的组合,使用继承会使得子类急剧膨胀。装饰模式正是用来解决这类问题的,对对象功能进行任意的扩展而不用担心类继承所带来的膨胀。

    现实中的例子:

         “疯狂坦克”,我想大家都玩过吧,每一关中都会随机爆出一些可吃的东西,譬如防护罩:坦克吃了,增强抗打能力;譬如子弹:坦克吃了,发射子弹的速度快了;譬如石墙:坦克吃了可以打碎石墙……

    解决方案:

          如果叫我实现这些功能(防护罩:坦克吃了,增强抗打能力;譬如子弹:坦克吃了,发射子弹的速度快了;譬如石墙:坦克吃了可以打碎石墙),我该如何实现呢?

         方案一:

        

         思路:很显然,不管坦克吃了什么东西变成什么东西,它最终还是一个坦克。只不过根据所吃东西的不同发射子弹后产生的效果不同而已。那么继承无疑是很好的选择。

        类图:

       

        代码片段

       

     public abstract class Itank
    {
    public int HP;
    public string Name;
    public Itank(string name)
    {
    this.Name = name;
    }
    public abstract void Shot(Itank destination);
    public virtual void BeAttacked(int hp)
    {
    this.HP -= hp;
    Console.WriteLine(string.Format("{0} is Attacked;Hp is {1}",Name, HP));
    }
    }
    public class CommonTank:Itank
    {
    public CommonTank(string name):base(name)
    {
    this.HP = 100;
    }
    public override void BeAttacked(int hp)
    {
    base.BeAttacked(hp);

    }
    public override void Shot(Itank destination)
    {
    Console.WriteLine(string.Format("{0} shot {1}", Name, destination.Name));
    destination.BeAttacked(20);
    }
    }
    public class SpecialTank:Itank
    {
    public SpecialTank(string name):base(name)
    {
    this.HP = 150;
    }
    public override void BeAttacked(int hp)
    {
    base.BeAttacked(hp);
    }
    public override void Shot(Itank destination)
    {
    Console.WriteLine(string.Format("{0} shot {1}", Name, destination.Name));
    destination.BeAttacked(50);
    }
    }
    public class CommonlHoodTank:Itank
    {
    public CommonlHoodTank(string name)
    : base(name)
    {
    this.HP = 300;
    }
    public override void BeAttacked(int hp)
    {
    base.BeAttacked(hp);
    }
    public override void Shot(Itank destination)
    {
    Console.WriteLine(string.Format("{0} shot {1}", Name, destination.Name));
    destination.BeAttacked(20);
    }
    }
    class Program
    {
    static void Main(string[] args)
    {
    CommonTank commonTank1 = new CommonTank("CommonTank1");
    CommonTank commomTank2 = new CommonTank("CommonTank2");
    commonTank1.Shot(commomTank2);
    Console.WriteLine("#####################################");
    SpecialTank specialTank1 = new SpecialTank("SpecialTank1");
    commonTank1.Shot(specialTank1);
    Console.WriteLine("#####################################");
    CommonlHoodTank commonlHoodTank1 = new CommonlHoodTank("CommonlHoodTank1");
    commonTank1.Shot(commonlHoodTank1);
    }
    }


       分析:如上代码,类CommonTank:普通坦克,SpecialTank:重型坦克。普通坦克吃了防护罩就需要生成新类:CommonlHoodTank。

      普通坦克吃了子弹就需要生成新类:CommonBulletTank。普通坦克既吃了防护罩又吃了子弹就需要生成新类:CommonHoodBulletTank......

      显然随着吃的东西的不同就需要生成不同的坦克子类。

      你可以认为吃了不同的东西后变成了新的坦克(用新类表示新坦克)以此来表示新的能力,但是子类复子类,子类何其多!!!不只是如此,坦克的新功能是随着吃的东西的不同而动态获得的,所以无需像继承一样在编译时就知道其能力。基于此,设计第二套方案。

     方案二:

         思路:坦克吃了东西后,其能力加强了,但是本质还是该坦克而不是新坦克。

         类图

        

       代码片段

       public abstract class Itank
        {
            public int HP;
            public  string Name;
            public Itank(string name)
            {
                this.Name = name;
            }
            public abstract void Shot(Itank destination);
            public virtual void BeAttacked(int hp)
            {
                this.HP -= hp;
                Console.WriteLine(string.Format("{0} is Attacked;Hp is {1}",Name, HP));
            }
        }
      public abstract class TankDecorator:Itank
        {
            protected Itank _itank;
            public TankDecorator(Itank itank,string name):base(name)
            {
                this._itank = itank;
            }
            public override void BeAttacked(int hp)
            {
                base.BeAttacked(hp);
            }
        }
      public BulletDecorator(Itank tank, string name)
                : base(tank, name)
            {
    
            }
            public override void BeAttacked(int hp)
            {
                _itank.BeAttacked(hp);
            }
            public override void Shot(Itank destination)
            {
                Console.WriteLine(string.Format("{0} shot {1}", Name, destination.Name));
                destination.BeAttacked(120);
            }
     public class HoodDecorator:TankDecorator
        {
            public HoodDecorator(Itank tank, string name)
                : base(tank, name)
            {
                tank.HP += 200;
            }
            public override void BeAttacked(int hp)
            {
                _itank.BeAttacked(hp);
            }
            public override void Shot(Itank destination)
            {
                Console.WriteLine(string.Format("{0} shot {1}", Name, destination.Name));
                destination.BeAttacked(20);
            } 
        }
    class Program
        {
            static void Main(string[] args)
            {
                CommonTank commonTank1 = new CommonTank("CommonTank1");
                CommonTank commomTank2 = new CommonTank("CommonTank2");
                commonTank1.Shot(commomTank2);
                
                Console.WriteLine("#####################################");
                HoodDecorator commonlHoodTank1 = new HoodDecorator(commonTank1, "CommonlHoodTank1");
                commonTank1.Shot(commonlHoodTank1);
            }
      }
    

     如上代码所示:不仅解决了子类多得问题,也解决了动态添加功能的问题。这就是装饰者模式之精髓。

    适用性

      在以下情况下应当使用装饰模式:

      1.需要扩展一个类的功能,或给一个类增加附加责任。

      2.需要动态地给一个对象增加功能,这些功能可以再动态地撤销。

      3.需要增加由一些基本功能的排列组合而产生的非常大量的功能,从而使继承关系变得不现实。

    总结

          Decorator模式采用对象组合而非继承的手法,实现了在运行时动态的扩展对象功能的能力,而且可以根据需要扩展多个功能,避免了单独使用继承带来的“灵活性差”和“多子类衍生问题”。同时它很好地符合面向对象设计原则中“优先使用对象组合而非继承”和“开放-封闭”原则。

  • 相关阅读:
    [解题报告]256 Quirksome Squares
    [解题报告]369 Combinations
    [解题报告]10696 f91
    [解题报告]483 Word Scramble
    [解题报告]10041 Vito's Family
    [解题报告]686 Goldbach's Conjecture (II)
    [解题报告]151 Power Crisis
    [解题报告]10079 Pizza Cutting
    [解题报告]495 Fibonacci Freeze
    [解题报告]541 Error Correction
  • 原文地址:https://www.cnblogs.com/smallstone/p/2209584.html
Copyright © 2020-2023  润新知