上次通过模拟发射***过程,为大家展现了一下策略模式的好处,此次来为大家介绍一下装饰模式。字面意思上来理解,就是为你想要展现的东西添加附加的功能。同样,这里还是不去具体介绍装饰模式的具体意思,先通过模拟一个场景来让大家有一个直观的认识。

还记得上次发射***的那个场景么?我们可以通过更换***,来达到发射不同***的效果。这次我们对场景做一些变化,我们吃到新的***的时候,不去替换原来的***,而是为原来的***增加新的效果。

同样,我们先用面向过程的方式来实现一下,然后再一点一点的封装,重构。

首先,我们需要一个发射***的方法Fire()。

        static void Fire(List
 listBullet)        {            foreach (string bullet in listBullet)            {                if ("S".Equals(bullet))                {                    Console.WriteLine("加强散弹");                }                else if ("C".Equals(bullet))                {                    Console.WriteLine("发射普通***");                }            }        }

ListBullet存放当前的***情况。接下来是场景类。

        static void Main(string[] args)        {            List
 listBullet = new List
();            //默认一开始装备普通***            listBullet.Add("C");            Fire(listBullet);            //加强散弹威力            listBullet.Add("S");            Fire(listBullet);             Console.ReadKey();        }

运行得到结果

发射普通***

发射普通***

加强散弹

将其修改为面向对象的方式,由于我们在上一次已经有过封装的经验了,所以我们这次就直接封装出PeopleBullet类出来。

    public class Bullet    {        private List
 listBullet = new List
();         public Bullet()        {            this.listBullet.Add("C");        }         public void AddBullet(string status)        {            this.listBullet.Add(status);        }         public void Fire()        {            foreach (string bullet in listBullet)            {                if ("S".Equals(bullet))                {                    Console.WriteLine("加强散弹");                }                else if ("C".Equals(bullet))                {                    Console.WriteLine("发射普通***");                }            }        }}

 

    public class People    {        private Bullet bullet = new Bullet();         public void AddBullet(string b)        {            bullet.AddBullet(b);        }         public void Fire()        {            bullet.Fire();        }    }

场景类更改为

            People people = new People();            //默认一开始装备普通***            people.Fire();            //加强散弹威力            people.AddBullet("S");            people.Fire();

测试结果不变。

接下来我们要考虑一下怎样将BulletFire方法中的if/else语句替换掉了,是不是又想起了策略模式?建立一个***接口,然后将普通***和加强散弹威力的实现类继承他,将来再扩展加强激光威力的***。只不过就是把原来单独的散弹和激光***的类换成了加强威力的类。看起来是一种好的设计,可是如果我即想要加强散弹威力,又想要加强激光威力呢?再写一个同时加强这两种威力的类?如果我要是又来了一个加强***射速威力的需求呢?你的类是不是就有点太多了。为了避免这种情况,我们将继承改为聚合。

***接口及各实现类,激光***与散弹相同,省略。

public interface IBullet    {        void Fire();    }     public class CommonBullet : IBullet    {        public void Fire()        {            Console.WriteLine("发射普通***");        }}     public class ScatterBullet : IBullet    {        IBullet bullet;         public ScatterBullet(IBullet bullet)        {            this.bullet = bullet;        }         public void Fire()        {            this.bullet.Fire();            Console.WriteLine("加强散弹");        }    }

我们在散弹中保留一个***接口的对象,这样我们在发射散弹***的时候,就可以先调用被加强威力的***的fire方法了(这个动作就是装饰模式的核心)。

接下来,People

    public class People    {        private IBullet bullet = new CommonBullet();         public void UpLevelScatterBullet()        {            bullet = new ScatterBullet(bullet);        }         public void UpLevelLaserBullet()        {            bullet = new LaserBullet(bullet);        }         public void Fire()        {            this.bullet.Fire();        }}

场景调用

            People people = new People();            //默认一开始装备普通***            people.Fire();            //加强散弹威力            people.UpLevelScatterBullet();            people.Fire();            people.UpLevelLaserBullet();            people.Fire();

运行结果和我们预期的一样。去掉了if/elsefor循环,用多态的方式实现,将代码的阅读复杂度降低。同时提供很好的扩展功能,比继承有更好的灵活性。

写到这里,装饰模式基本已经改变完成,之所以说基本改变完成,是因为从装饰模式的一般类图上来说,需要一个抽象装饰对象,来对装饰对象进行一个抽象。添加抽象装饰对象的好处是可以对装饰对象的共同点更好的封装。个人认为这一步并不是必须的,是否要添加根据需求而定。

    public abstract class StrengthenBullet : IBullet    {        protected IBullet bullet;         public abstractvoid Fire();}

添加抽象类StrengthenBullet,使其继承IBullet接口。

public class ScatterBullet : StrengthenBullet

而原来的散弹类和激光弹类则继承StrengthenBullet。我们可以看到,由于抽象装饰类中声明了bullet对象,子类中就不需要再做声明了。

总结:

装饰模式中需要一个真实对象和一些装饰对象,他们都继承形同的接口,这样场景中就可以对真实对象和装饰对象用相同的方式交互。装饰对象保留一个真实对象的引用。在被调用的时候,装饰对象就可以通过引用调用真实对象的具体方法,并可以在调用前后添加附加的功能。

其实这个程序写到这里还没有结束,我们可以看到,现在的程序依然有一些比较繁琐的地方,比如每当我要添加一个新的***的时候,我还要在People类中添加一个加强该威力的方法,我们可不可以将这一过程抽象出来?此外如果我们的装饰对象有一些共同的地方,是不是需要些很多次一样的?关于此类问题的解决,我想留到下次模版方法的时候再与大家分享。