步步为营 .NET 设计模式学习笔记 十八、Template(模板模式)
概述
变化一直以来都是软件设计的永恒话题,在XP编程中提倡拥抱变化,积极应对。如何更好的去抓住变化点,应对变化?如何更好的提高代码复用?通过学习Template Method模式,您应该有一个新的认识。
意图
定义一个操作中的算法的骨架,而将一些步骤延迟到子类中。Template Method使得子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤。[-GOF《设计模式》]
结构图
图1 Template Method 模式结构图
生活中的例子
模板方法定义了一个操作中算法的骨架,而将一些步骤延迟到子类中。房屋建筑师在开发新项目时会使用模板方法。一个典型的规划包括一些建筑平面图,每个平面图体现了不同部分。在一个平面图中,地基、结构、上下水和走线对于每个房间都是一样的。只有在建筑的后期才开始有差别而产生了不同的房屋样式。
图2 使用建筑图为例子的Template Method模式
示例用例图
定义一个超市抽象类,每个超市继承这个抽象类.形成我们的模板模式,用例图如下:
代码设计
先创建ISuperMarket.cs:
public interface ISuperMarket { /// <summary> /// 活动开始时间 /// </summary> /// <returns></returns> DateTime PreferencesStartTime(); /// <summary> /// 种类 /// </summary> /// <returns></returns> int Category(); /// <summary> /// 优惠折扣 /// </summary> /// <returns></returns> double Discount(); /// <summary> /// 超市名称 /// </summary> /// <returns></returns> string Name(); /// <summary> /// 显示信息 /// </summary> /// <returns></returns> string ShowInfo(); }
再创建SuperMarket.cs:
public abstract class SuperMarket : ISuperMarket { #region ISuperMarket 成员 public abstract DateTime PreferencesStartTime(); public abstract int Category(); public abstract double Discount(); public abstract string Name(); public string ShowInfo() { StringBuilder strBuilder = new StringBuilder(); strBuilder.AppendFormat("{0}在{1}有{2}种商品享受{3}折优惠.赶快抢购!\n", Name(), PreferencesStartTime().ToShortDateString(), Category().ToString(), (Discount() * 10).ToString()); return strBuilder.ToString(); } #endregion }
再创建WalMart.cs:
public class WalMart : SuperMarket { public override DateTime PreferencesStartTime() { return DateTime.Now.AddDays(7); } public override int Category() { return 100; } public override double Discount() { return 0.8; } public override string Name() { return "沃尔玛"; } }
再创建GoodAndMore.cs:
public class GoodAndMore : SuperMarket { public override DateTime PreferencesStartTime() { return DateTime.Now.AddDays(7); } public override int Category() { return 150; } public override double Discount() { return 0.85; } public override string Name() { return "好又多"; } }
最后再调用
public partial class Run : Form { public Run() { InitializeComponent(); } private void btnRun_Click(object sender, EventArgs e) { //------------------------------------- ISuperMarket superMarket = new Template.WalMart(); rtbResult.AppendText(superMarket.ShowInfo()); superMarket = new GoodAndMore(); rtbResult.AppendText(superMarket.ShowInfo()); } }
结果如下图:
实现要点
1.Template Method模式是一种非常基础性的设计模式,在面向对象系统中有着大量的应用。它用最简洁的机制(虚函数的多态性)为很多应用程序框架提供了灵活的扩展点,是代码复用方面的基本实现结构。
2.除了可以灵活应对子步骤的变化外,“不用调用我,让我来调用你”的反向控制结构是Template Method的典型应用。
3.在具体实现方面,被Template Method调用的虚方法可以具有实现,也可以没有任何实现(抽象方法,纯虚方法),但一般推荐将它们设置为protected方法。[李建忠]
4.复用算法的骨架,将可变的实现细节留给子类实现。
5.留给子类实现的方法需要在父类中定义,可以是抽象方法也可以是带有默认实现的方法。
适用性
1.一次性实现一个算法的不变的部分,并将可变的行为留给子类来实现。
2.各子类中公共的行为应被提取出来并集中到一个公共父类中以避免代码重复。这是Opdyke和Johnson所描述过的“重分解以一般化”的一个很好的例子。首先识别现有代码中的不同之处,并且将不同之处分离为新的操作。最后,用一个调用这些新的操作的模板方法来替换这些不同的代码。
3.控制子类扩展。模板方法只在特定点调用“Hook”操作,这样就只允许在这些点进行扩展。
4.如果某些类型的操作拥有共同的实现骨架和不同的实现细节的话,可以考虑使用模版方法来封装统一的部分。
总结
1.Template Method模式是非常简单的一种设计模式,但它却是代码复用的一项基本的技术,在类库中尤其重要。
2.模版方法可以说是最不像设计模式的设计模式,通常很多设计模式会和模版方法一起使用。