模板方法模式:在一个方法中定义一个算法的骨架,而将一些步骤延迟到子类中。模板方法使得子类能够在不改变算法结构的状况下,从新定义算法中的某些步骤。算法
从定义中,应该能够看出一部分,为了更好理解,下面就直接上例子:设计模式
在敲代码时,累了喝杯咖啡或者喝杯茶,会精神倍增。其实不管咖啡仍是茶在冲的时间都是有讲究的。这个在本文不是重点。下面分别描述一下冲泡咖啡和冲泡茶的过程:ide
两种茶其分别的作法以下代码:测试
1 public class Coffee 2 { 3 public void PrepareRecipe() 4 { 5 //烧水 6 BoilWater(); 7 //冲咖啡 8 BrewCoffeeGrinds(); 9 //倒入茶杯中 10 PourInCup(); 11 //加入糖和咖啡 12 AddSugarAndMilk(); 13 } 14 public void BoilWater() 15 { 16 Console.WriteLine("烧水"); 17 } 18 19 public void BrewCoffeeGrinds() 20 { 21 Console.WriteLine("冲咖啡"); 22 } 23 24 public void PourInCup() 25 { 26 Console.WriteLine("倒入杯子中"); 27 } 28 29 public void AddSugarAndMilk() 30 { 31 Console.WriteLine("加糖和牛奶"); 32 } 33 } 34 35 public class Tea 36 { 37 public void PrepareRecipe() 38 { 39 //烧水 40 BoilWater(); 41 //泡茶 42 SteepTeaBag(); 43 //倒入茶杯中 44 PourInCup(); 45 //加入柠檬 46 AddLemon(); 47 } 48 public void BoilWater() 49 { 50 Console.WriteLine("烧水"); 51 } 52 53 public void SteepTeaBag() 54 { 55 Console.WriteLine("泡茶"); 56 } 57 58 public void PourInCup() 59 { 60 Console.WriteLine("倒入杯子中"); 61 } 62 63 public void AddLemon() 64 { 65 Console.WriteLine("加柠檬"); 66 } 67 }
在面向对象语言中,是要讲求复用的,如今烧水和带入杯子的方法显然是重复的,这样就不符合对象村的村规——复用。
对比两种作法,都是须要四个步骤,能不能把相同的使用一个基类,不一样的部分分别由本身的去实现。其类图以下编码
若是再细致观察的话,咱们的冲咖啡和泡茶以及加入咖啡和牛奶都是属于差很少动做相同的。因此能够继续抽象,抽象后的方法大体如此:spa
其实得出的也就是咱们的模板方法。下面来看看模板方法模式的类图:设计
3、模板方法类图3d
从类图能够看到咱们把共用的方法放在抽象类中,用于复用。把不肯定的方法,放入到具体类中,以便让具体类能够很好的构造本身的方法。除此以外,还有个重点是不管是构造好的方法也好,仍是抽象的方法也好,都会被装入到一个TemplateMethod方法中。就像咱们平时的作项目也是如此,大体的步骤须要:需求分析——>编码——>测试,这些最基本的过程。可是具体的每一步,可能都是不一样的,可是作项目的过程的几个步骤是基本不变的,把步骤抽象成了模板,之后作项目的时间,都按这个步骤去作。这个就是咱们的模板方法模式。到这里仍是把咱们的茶和咖啡沏好。code
基类代码:对象
public abstract class CoffeeinBeverage1 { public void BoilWater() { Console.WriteLine("烧水"); } public void PrepareRecipe() { BoilWater(); Brew(); PourInCup(); AddCondiments(); } public void PourInCup() { Console.WriteLine("倒入杯子中"); } public abstract void Brew(); public abstract void AddCondiments(); }
咖啡代码:
public class Coffee1 : CoffeeinBeverage1 { public override void Brew() { Console.WriteLine("冲咖啡"); } public override void AddCondiments() { Console.WriteLine("加糖和牛奶"); }
茶代码:
public class Tea1 : CoffeeinBeverage1 { public override void Brew() { Console.WriteLine("泡茶"); } public override void AddCondiments() { Console.WriteLine("加柠檬"); } }
在上面的茶水和咖啡中,如今是看起来能喝了,可是有个问题就是有些人喝咖啡喜欢不加任何调味料的。那么咱们硬是给客人加,确定是会生气的。为了知足这个要求,设计模式提供的有这个解决方案——使用钩子。具体什么是钩子,咱们小的时间的课本上有个猴子捞月亮的图片:
每一个猴子不只会被其余猴子拉住,还会伸出一个手去拉其余猴子。但最后一个手是没有拉其余猴子了,可是仍是要伸下去的,以防碰不到月亮。
一样不知道方法能不能用获得,可是有对应的方法,具体的什么时间用获得,什么时间用不到,根据条件来判断。
下面来看看钩子如何在模板方法模式中使用的:
基类代码:
public void PrepareRecipe() { BoilWater(); Brew(); PourInCup(); //有个判断方法来添加调料 if (WantCondiments()) { AddCondiments(); } } /// <summary> /// 加入一个方法,用来判断是否须要加调料 /// </summary> /// <returns></returns> public virtual bool WantCondiments() { return true; }
在子类中,能够经过不一样的方式类覆盖WantCondiment()方法。用来表示是否要加调料的标准,在此方法中注意必须加入virtual关键字,以便子类中使用override重写。下面看看咖啡中的方法:
public override bool WantCondiments() { return false; }
下面是测试代码:
class Program { static void Main(string[] args) { CoffeeinBeverage1 coffeninBeverage = new Coffee1(); coffeninBeverage.PrepareRecipe(); Console.ReadKey(); } }
输出结果:
发现已经去掉了调料。
在看模板方法的时间,很容易想到工厂方法。由于他们都是让具体的实现放在子类中,可是工厂方法主要是生产出产品,而后去应用产品。模板方法是在于依赖子类中的步骤中的其中几个步骤,具体的步骤已经在基类中写好了。
一样模板方法模式和策略模式都是封装算法。可是策略模式中的每一个策略都是单独的一个类。能够随时去更改策略。模板方法模式虽然也是封装了算法,其实主要在于封装步骤,具体的实现是根据依靠各个子类。
除此以外,模板方法模式还涉及到一个好莱坞原则:
不要给我打电话,我会主动和你打电话。
在模板方法模式中扮演好莱坞的角色是抽象类,子类是演员的角色。
通常须要调用子类中的方法都已经在模板中定义好了,须要时,会主动联系各个步骤,最好在子类中不要去调用抽象类中的方法具体方法,以防止子类和父类的调用的凌乱。
本文主要先列出了模板方法模式的定义,而后经过例子来帮助理解模式,接着提出钩子在模板方法模式中的使用,最后简单对比了模板方法模式、策略模式以及工厂方法模式。