设计模式3.Template Method(模板方法)

源码地址 https://github.com/sadgeminids/TemplateMethodLearning

 

设计模式目的:

模板方法,在父类中定义好算法步骤顺序,将算法中的某一具体部分延迟到子类中实现,使得可以在不改变算法的前提下,将体重特定的部分改变实现。

 

XML类图:

 

模式核心概述:

从模式描述中就能知道,模板方法模式的类图很简单,仅涉及到父类和子类。

1.父类实现一个模版方法,定义具体的算法;

2.父类提供算法中可变部分的接口,供子类实现;

3.子类实现可变部分的接口

 

代码实现:

1.实现父类算法,提供抽象接口

namespace TemplateMethod
{
    public class Weekend
    {
        public void Plain()
        {
            GetUp();
            Sleep();
            DoSomething();
        } 

        public virtual void GetUp()
        {

        }

        public virtual void Sleep()
        {

        }

        public virtual void DoSomething()
        {

        }
    }
}

Plain就是一个模板方法,定义了周末一天的计划。但是每个步骤具体要怎么做,又没有完全限定死,而是做成了抽象接口供子类自由实现。

2.子类具体实现

实现一个悠闲的周末类:

namespace TemplateMethod
{
    public class LeisureWeekend : Weekend
    {
        public override void GetUp()
        {
            Console.WriteLine("Get up at 11 am ");
        }

        public override void Sleep()
        {
            Console.WriteLine("Sleep at 10 pm");
        }

        public override void DoSomething()
        {
            Console.WriteLine("Watch movie");
            Console.WriteLine("Play Game");
            Console.WriteLine("Riding");
        }


    }
}

再实现一个忙碌的周末类:

namespace TemplateMethod
{
    public class BusynessWeekend : Weekend
    {
        public override void GetUp()
        {
            Console.WriteLine("Get up at 9 am ");
        }

        public override void Sleep()
        {
            Console.WriteLine("Sleep at 12 pm");
        }

        public override void DoSomething()
        {
            Console.WriteLine("Work...");
            Console.WriteLine("Work...");
            Console.WriteLine("Work...");
        }
    }
}

3.调用代码:

namespace TemplateMethod
{
    class Program
    {
        static void Main(string[] args)
        {
            Weekend leisure = new LeisureWeekend();
            leisure.Plain();

            Weekend busyness = new BusynessWeekend();
            busyness.Plain();

            Console.ReadKey();
        }
    }
}

 

从代码也可看出模板方法模式很简单,用不了多少代码就能实现这个模式的实例。

其实,在实际工程中,绝大部分项目中或多或少都用到了该模式。因为这种模式实在是太基础,太易于被使用了。

举个简单例子,比如在卡牌游戏中,有冒险、金币、常规关卡、爬塔等几个模式,从点击主界面按钮到真正进入游戏,虽然流程基本一致,但每个模式打开界面读表、奖励提示等数据来源不一致,这时候就可以使用模板方法。

ClickButton();                               // 点击某个模式按钮

OpenRewardsWindow();              // 打开不同模式的奖励界面,读取不同的奖励表

PreEnterProcess();                       // 进入副本前的操作,比如扣体力、加载一些配置表

EnterMap();                                  // 开始进入地图

...

 

实现一个虚函数就是模板方法了吗?!

从某种意义上可以这样说,父类的一个虚函数本来从广义上来讲就是一个算法,这样只要有子类去实现了这个接口就可以算得上是个模板方法。但如果真这样,模板方法也就太廉价了。

在GOF的《设计模式》一书中,讲模板方法归类为行为类模式。所谓行为类,蕴含了两方面含义。

1.行为--意指这种模式重点关注的是行为

2.类--这种行为是在类中间传递的

行为如何在类中传递,只能是继承。模板方法可变部分就是在子类和父类中传递的行为。

初学者可能会将关注点放在虚函数的实现这个点上,然而就如名字一样,模板方法才是这个设计模式的真正核心价值所在。将一个算法归纳,不变的部分放入父类,将可变的部门抽象出来,用虚函数实现供子类复写。如何去设计好这个算法,去剥离可变、不可变两部分才是重点分析设计所在。如何设计好这个模板方法,关系到这个模式的稳定性。

如果仅仅是一个虚函数,可以说只包含了可变实现,并没有通用不变的部分,这样还算是一个合理的算法吗?

所以,设计好算法,尤其是某个算法整体流程非常固定,几乎不会改变,只是某部门实现比较多变灵活,不妨考虑下模版方法。

最后,用一个关联模式,回溯下之前的内容同时,也再将模板设计模式深刻入脑中。

设计模式1 Iterator模式中,我们有提到Foreach内部做了些操作,它需要一个IEnumerator对象,并且调用IEnumerator对象的Current属性和MoveNext接口。看着是不是很眼熟呢?Foreach是不是就有点像一个模版方法?!只是说这个模版方法被C#语言给隐藏起来了,但它的算法是固定了的。

我们可以大致模拟一下foreach的代码:

// foreach
IEnumerator enumerator = XXXX.GetEnumerator();
while(enumerator.MoveNext()){
    var value = enumerator.Current;
}

这就是一个类似的模版方法,算法是固定的,MoveNext和Current是抽象可变的。不过略有区别的是,这个模版方法是属于类的外部方法,不是在IEnumerator类的内部。算法和抽象在不同类中实现,所以IEnumerator不能算作是实现了模版设计模式的一个类。

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值