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

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

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

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

        static void Fire(List<string> 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<string> listBullet = new List<string>();
            //默认一开始装备普通***
            listBullet.Add("C");
            Fire(listBullet);
            //加强散弹威力
            listBullet.Add("S");
            Fire(listBullet);
 
            Console.ReadKey();
        }


运行得到结果

发射普通***

发射普通***

加强散弹


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

    public class Bullet
    {
        private List<string> listBullet = new List<string>();
 
        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语句替换掉了,是不是又想起了策略模式?建立一个***接口,然后将普通***和加强散弹威力的实现类继承他,将来再扩展加强激光威力的***。只不过就是把原来单独的散弹和激光***的类换成了加强威力的类。看起来是一种好的设计,可是如果我即想要加强散弹威力,又想要加强激光威力呢?再写一个同时加强这两种威力的类?如果我要是又来了一个加强***射速威力的需求呢?你的类是不是就有点太多了。为了避免这种情况,我们将继承改为聚合。

wKiom1PhjuLDyFfKAACkF__AyrU417.jpg

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

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类中添加一个加强该威力的方法,我们可不可以将这一过程抽象出来?此外如果我们的装饰对象有一些共同的地方,是不是需要些很多次一样的?关于此类问题的解决,我想留到下次模版方法的时候再与大家分享。