模板方法模式的定义:
一个方法定义一个算法的骨架 ,而将一些步骤延迟到子类中。
Template Method使得子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤。
模板方法模式是一种行为型模式,当执行某个操作的步骤基本上是固定的是,可以使用模板方法模式。
模板方法模式定义了一个算法框架,将一系列具体的操作交给子类中去实现。
可以在不改变模板框架算法的情况下,灵活的扩展改变每一步的具体实现。
模板方法模式主要包括三个方面:
抽象类:负责定义算法框架和模板方法的实现
具体子类:负责首相类中各个抽象方法的具体的实现
模板方法:执行算法方法的顺序
通过使用模板方法模式,我们可以将算法的结构与实现分离,
提高代码的可读性和可维护性,并且可以方便地扩展和修改算法的行为。
先看一个泛示例 ,它不是一个实际的例子,但读者很容易看懂:
using System;
namespace TemplateMethodPatternExample
{
// 定义抽象类,包含模板方法和基本操作
public abstract class AbstractClass
{
// 模板方法
public void TemplateMethod()
{
Console.WriteLine("Template method start");
// 调用基本操作1
BasicOperation1();
// 调用基本操作2
BasicOperation2();
Console.WriteLine("Template method end");
}
// 定义抽象基本操作1
protected abstract void BasicOperation1();
// 定义抽象基本操作2
protected abstract void BasicOperation2();
}
// 实现抽象类,定义具体操作
public class ConcreteClass1 : AbstractClass
{
protected override void BasicOperation1()
{
Console.WriteLine("ConcreteClass1 - BasicOperation1");
}
protected override void BasicOperation2()
{
Console.WriteLine("ConcreteClass1 - BasicOperation2");
}
}
public class ConcreteClass2 : AbstractClass
{
protected override void BasicOperation1()
{
Console.WriteLine("ConcreteClass2 - BasicOperation1");
}
protected override void BasicOperation2()
{
Console.WriteLine("ConcreteClass2 - BasicOperation2");
}
}
class Program
{
static void Main(string[] args)
{
// 创建具体子类对象并调用模板方法
AbstractClass obj1 = new ConcreteClass1();
obj1.TemplateMethod(); // 输出:Template method start,ConcreteClass1 - BasicOperation1,ConcreteClass1 - BasicOperation2,Template method end
AbstractClass obj2 = new ConcreteClass2();
obj2.TemplateMethod(); // 输出:Template method start,ConcreteClass2 - BasicOperation1,ConcreteClass2 - BasicOperation2,Template method end
}
}
}
例子:泡茶
当我们在泡茶时,泡茶的顺序基本上都是一致的,如:
烧水、拿茶叶、倒茶、等待、品茶、洗锅。
可以看出,除了拿茶叶究竟是红茶还是绿茶或者是果茶不一样,其他的步骤全部相同,
这时就可以使用模板方法模式了。
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace 模板方法模式_泡茶
{
class Program
{
static void Main(string[] args)
{
中国式泡茶 p =new 中国式泡茶();
Console.WriteLine();
日本式泡茶 p1 = new 日本式泡茶();
Console.ReadKey();
}
}
public class 中国式泡茶 : 泡茶
{
//public 中国式泡茶():base()
//{
//}
public override void 倒茶()
{
Console.WriteLine("长水壶杂技式倒茶");
}
public override void 拿茶叶()
{
Console.WriteLine("拿毛尖茶");
}
}
public class 日本式泡茶 : 泡茶
{
//public 中国式泡茶():base()
//{
//}
public override void 拿茶叶()
{
Console.WriteLine("拿日本茶");
}
}
public abstract class 泡茶
{
public 泡茶()
{
烧水();
拿茶叶();
倒茶();
等待();
品茶();
品茶();
冼锅();
}
public virtual void 烧水()
{
Console.WriteLine("烧水");
}
//拿茶叶要根据不同的茶拿不同的茶叶,由子类实现
public abstract void 拿茶叶();
public virtual void 倒茶()
{
Console.WriteLine("倒茶");
}
public virtual void 等待()
{
Console.WriteLine("等待");
}
public virtual void 品茶()
{
Console.WriteLine("品茶");
}
public virtual void 冼锅()
{
Console.WriteLine("冼锅");
}
}
}模块方法的勾子:
上面的方法“烧水、拿茶叶、倒茶、等待、品茶、洗锅”都设计成vitual方法,子类可以视情况决定要不要重写它;
钩子是一种被声明在抽象类中的方法,当只有空或者默认的实现。
钩子的存在可以让子类有能力对算法的不同点进行挂钩,要不要挂钩由子类决定。
上面的例子中,中国泡茶的“倒茶”方式与日本泡茶有明显的不同之处,因此这里埋下勾子,方便子类差异化。
一个实际项目的例子:
如果不在工作中思考,模板方法模式的概念和定义即使背再多遍估计也是徒劳,
今天我思考如何解决实际项目中的问题时发现,模板方法正是解决问题的良策。
需求
我们项目中要实现一个创建会议的需求,但我们系统可设置将会议同步到其他第三方系统中。
而在创建会议前要经过几个必须的步骤,比如验证冲突,计算循环规则,对比时间段是否符合等。
思路
不同点
创建会议的方式不同:
本地系统创建
第三方系统创建
相同点
验证会议是否冲突
根据循环规则计算出预定时间段
是否符合预定规则
特殊点
即使会议又冲突,但有需求是依然要能创建成功!所以验证冲突步骤是可选的。
业务梳理后如下:

总结
模板方法模式 有骨架接口,子流程通过virtual关键字暴露给子类重写,调用时晚绑定;
即 子类可以重写父类的子流程,使父类流程丰富;本质是通过固定骨架约束子类的行为。这是最常用的设计模式。
模板方法模式是一个非常常见的模式,到处都是。
---------------------
作者:hackpig
来源:www.skcircle.com
版权声明:本文为博主原创文章,转载请附上博文链接!


少有人走的路


















