模版方法模式,个人认为还是用处比较多的一个设计模式,而且也是比较好学和理解的一个。依然来通过模拟一个场景来慢慢了解。
现在我们来实现一下泡茶这个过程。首先我们需要烧开一壶水,然后往茶壶中放茶叶,加入开水,等待茶泡好。
经过前两次的分享,大家应该具备了基本的面向对象的思想了,这里就不再用面向过程的方式演示了。
首先,有一种普通人,他泡茶的方式是这样的
public class Common { public void MakeTea() { HeatUpWater(); PutTea(); PutWater(); } private void HeatUpWater() { Console.WriteLine("把水煮开"); } private void PutTea() { Console.WriteLine("放茶叶"); } private void PutWater() { Console.WriteLine("加水冲泡,等待"); } }
好,现在我又有了一种文艺的泡茶方式
public class Art { public void MakeTea() { HeatUpWater(); PutTea(); PutWater(); } private void HeatUpWater() { Console.WriteLine("用山泉水,把水煮开"); } private void PutTea() { Console.WriteLine("放茶叶,用新茶"); } private void PutWater() { Console.WriteLine("先洗茶,再加水冲泡,等待"); } }
现在我们可以看到无论是普通还是文艺的方式,泡茶的步骤都是一样的,也就是说泡茶方法中的内容都是一样的,那么我们现在是不是可以将这部分一样的代码抽象出来。
我们可以把这部分代码放到一个新的类中,形成一种聚合的关系。
首先我们需要一个抽象类,来作为Common和Art的父类,以方便我们进行多态。
public abstract class AbstractMethod { public abstract void HeatUpWater(); public abstract void PutTea(); public abstract void PutWater(); }
Common和Art分别继承这个抽象类,并实现方法,同时去掉MakeTea方法,这里我们发现由于这些泡茶的方法需要被一个新的类访问,所以他的访问权限由原来的private变成了现在的public了,这是一个破坏封装的问题,我们之后再来解决。
接下来我们来写这个泡茶类。
public class MakeTea { public void Make(AbstractMethod method) { method.HeatUpWater(); method.PutTea(); method.PutWater(); } }
场景类中
class Program { static void Main(string[] args) { Art c = newArt(); MakeTea tea = new MakeTea(); tea.Make(c); Console.ReadKey(); } }
运行测试,与我们预想的一样。
怎么样,现在这种方式是不是有点眼熟。很像我们之前介绍的策略模式吧。虽然这样设计破坏了原来的访问结构,但我觉得其实设计没有绝对的对错,见仁见智。找到合适的就行。
接下来我们来用另一种方式,来尽量避免访问结构的破坏。
说起来很简单,我们只需要把原来Art和Method中的MakeTea方法上移到AbstractMethod父类中,同时将HeatUpWater,PutTea,PutWater三个方法的访问类型改为Protected。
public abstract class AbstractMethod { public void MakeTea() { HeatUpWater(); PutTea(); PutWater(); } protected abstract void HeatUpWater(); protected abstract void PutTea(); protected abstract void PutWater(); }
场景类中
static void Main(string[] args) { AbstractMethod m = new Art(); m.MakeTea(); Console.ReadKey(); }
运行测试,与我们预想的一致。这里虽然我们也将访问类型改为了protected,但总比public要好一些吧。这种把子类中公共的代码移到父类中的方法,就是我这次想和大家分享的模版方法。
这样做的好处就是如果需要再扩展其他的子类时,通用方法就不必再多些一次了,而且如果想要修改通用方法的话,也只需要修改父类中的那一个方法就可以了,不必修改每一个子类中的方法。
缺点应该就是在需要private的时候,破坏了访问结构,要不要这样使用,还是要看你的具体需求,是否能够承受这样的风险。
接下来我们再来回过头来看一下上次装饰模式中遗留的一些问题。
上次我们说到如果我们需要扩展一种***的时候,People类中还需要为该种***写一个升级方法,现在我们来看一下,能不能通过模版的方式,将这个升级方法提取到父类中。
我们现在***的抽象类中StrengthenBullet添加一个升级方法UpLevel
public IBullet UpLevel(IBullet bullet) { this.bullet = bullet; return this; }
这个方法中,我们将需要升级的***类型作为参数传进来,赋值给参数bullet,并且返回本身。相当于我们之前子类中的那个构造函数。
public ScatterBullet(IBullet bullet) { this.bullet = bullet; }
还有太捋明白的再仔细体会一下,不难理解。那么现在这个构造函数就没什么用了,可以删除了。
接下来People类中原来对应的升级方法,也可以删除了,更改为这样
public void UpLevelBullet(StrengthenBullet bullet) { this.bullet =bullet.UpLevel(this.bullet); } 场景类中在调用的时候也要做一点更改 people.UpLevelBullet(new ScatterBullet()); people.Fire(); people.UpLevelBullet(new LaserBullet()); people.Fire();
运行测试,与预想结果相同。
其实我也不清楚这样的做法是否算是模版方法,毕竟和通用的看起来不太像,我们只是借用了一下将子类通用代码提取到父类中的思想,不过实际上大多时候我们都不需要知道他是不是什么模式,只要我们能用它来增强扩展性,使程序结构看起来更好,就放心大胆的用吧(当然,如果后来发现用错了,也要及时修改过来哦!)。做到手上无招,心中也无招。
本文出自 “huwei_lfc” 博客,请务必保留此出处http://8061813.blog.51cto.com/8051813/1537161
原文地址:http://8061813.blog.51cto.com/8051813/1537161