上次通过模拟发射***过程,为大家展现了一下策略模式的好处,此次来为大家介绍一下装饰模式。字面意思上来理解,就是为你想要展现的东西添加附加的功能。同样,这里还是不去具体介绍装饰模式的具体意思,先通过模拟一个场景来让大家有一个直观的认识。
还记得上次发射***的那个场景么?我们可以通过更换***,来达到发射不同***的效果。这次我们对场景做一些变化,我们吃到新的***的时候,不去替换原来的***,而是为原来的***增加新的效果。
同样,我们先用面向过程的方式来实现一下,然后再一点一点的封装,重构。
首先,我们需要一个发射***的方法Fire()。
static void Fire(ListlistBullet) { foreach (string bullet in listBullet) { if ("S".Equals(bullet)) { Console.WriteLine("加强散弹"); } else if ("C".Equals(bullet)) { Console.WriteLine("发射普通***"); } } }
用ListBullet存放当前的***情况。接下来是场景类。
static void Main(string[] args) { ListlistBullet = new List (); //默认一开始装备普通*** listBullet.Add("C"); Fire(listBullet); //加强散弹威力 listBullet.Add("S"); Fire(listBullet); Console.ReadKey(); }
运行得到结果
发射普通***
发射普通***
加强散弹
将其修改为面向对象的方式,由于我们在上一次已经有过封装的经验了,所以我们这次就直接封装出People和Bullet类出来。
public class Bullet { private ListlistBullet = 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();
测试结果不变。
接下来我们要考虑一下怎样将Bullet中Fire方法中的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/else和for循环,用多态的方式实现,将代码的阅读复杂度降低。同时提供很好的扩展功能,比继承有更好的灵活性。
写到这里,装饰模式基本已经改变完成,之所以说基本改变完成,是因为从装饰模式的一般类图上来说,需要一个抽象装饰对象,来对装饰对象进行一个抽象。添加抽象装饰对象的好处是可以对装饰对象的共同点更好的封装。个人认为这一步并不是必须的,是否要添加根据需求而定。
public abstract class StrengthenBullet : IBullet { protected IBullet bullet; public abstractvoid Fire();}
添加抽象类StrengthenBullet,使其继承IBullet接口。
public class ScatterBullet : StrengthenBullet
而原来的散弹类和激光弹类则继承StrengthenBullet。我们可以看到,由于抽象装饰类中声明了bullet对象,子类中就不需要再做声明了。
总结:
装饰模式中需要一个真实对象和一些装饰对象,他们都继承形同的接口,这样场景中就可以对真实对象和装饰对象用相同的方式交互。装饰对象保留一个真实对象的引用。在被调用的时候,装饰对象就可以通过引用调用真实对象的具体方法,并可以在调用前后添加附加的功能。
其实这个程序写到这里还没有结束,我们可以看到,现在的程序依然有一些比较繁琐的地方,比如每当我要添加一个新的***的时候,我还要在People类中添加一个加强该威力的方法,我们可不可以将这一过程抽象出来?此外如果我们的装饰对象有一些共同的地方,是不是需要些很多次一样的?关于此类问题的解决,我想留到下次模版方法的时候再与大家分享。