首页 > 代码库 > 浅谈设计模式3-模板方法模式

浅谈设计模式3-模板方法模式

模版方法模式,个人认为还是用处比较多的一个设计模式,而且也是比较好学和理解的一个。依然来通过模拟一个场景来慢慢了解。

现在我们来实现一下泡茶这个过程。首先我们需要烧开一壶水,然后往茶壶中放茶叶,加入开水,等待茶泡好。

经过前两次的分享,大家应该具备了基本的面向对象的思想了,这里就不再用面向过程的方式演示了。

首先,有一种普通人,他泡茶的方式是这样的

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("先洗茶,再加水冲泡,等待");
        }
    }


现在我们可以看到无论是普通还是文艺的方式,泡茶的步骤都是一样的,也就是说泡茶方法中的内容都是一样的,那么我们现在是不是可以将这部分一样的代码抽象出来。

我们可以把这部分代码放到一个新的类中,形成一种聚合的关系。

首先我们需要一个抽象类,来作为CommonArt的父类,以方便我们进行多态。

public abstract class AbstractMethod
    {
public abstract void HeatUpWater();
 
public abstract void PutTea();
 
public abstract void PutWater();
    }


CommonArt分别继承这个抽象类,并实现方法,同时去掉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();
        }
    }


运行测试,与我们预想的一样。

怎么样,现在这种方式是不是有点眼熟。很像我们之前介绍的策略模式吧。虽然这样设计破坏了原来的访问结构,但我觉得其实设计没有绝对的对错,见仁见智。找到合适的就行。

接下来我们来用另一种方式,来尽量避免访问结构的破坏。

说起来很简单,我们只需要把原来ArtMethod中的MakeTea方法上移到AbstractMethod父类中,同时将HeatUpWaterPutTeaPutWater三个方法的访问类型改为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