首页 > 代码库 > 9 模板方法模式

9 模板方法模式

模板方法模式(Template Mothod):在一个方法中定义一个算法的骨架,而将一些步骤延迟到子类中。模板方法使得子类可以在不改变算法结构的情况下,重新定义算法中的某些步骤。

UML类图如下:

技术分享

要点有两个:原语操作PrimitiveOperation以及钩子Hook()方法。

代码举例

 

技术分享
 1 public abstract class CaffeineBeverage
 2     {
 3         public void PrepareRecipe()
 4         {
 5             BoilWater();
 6             Brew();
 7             PourInCup();
 8             AddCondiments();
 9             TakeOut();
10         }
11         public void BoilWater()
12         {
13             Console.WriteLine("Boil Water");
14         }
15         public abstract void Brew();
16         public void PourInCup()
17         {
18             Console.WriteLine("Pouring into Cup");
19         }
20         public abstract void AddCondiments();
21         //打包
22         public virtual void TakeOut(){}
23     }
24 
25     class Coffee : CaffeineBeverage
26     {
27         public override void Brew()
28         {
29             Console.WriteLine("Brew Coffee Grinds");
30         }
31 
32         public override void AddCondiments()
33         {
34             Console.WriteLine("Add Sugar and Milk");
35         }
36         public override void TakeOut()
37         {
38             Console.WriteLine("Will you take out? (y/n)");
39             string yn = Console.ReadLine();
40             if (yn == "y") Console.WriteLine("Take out");
41         }
42     }
43 
44     class Tea : CaffeineBeverage
45     {
46         public override void Brew()
47         {
48             Console.WriteLine("Steeping the Tea");
49         }
50 
51         public override void AddCondiments()
52         {
53             Console.WriteLine("Adding Lemon");
54         }
55     }
View Code

 

这里Brew()与AddCondiments()便是两个原语操作,要在子类中重写。因为冲泡茶叶和咖啡都有类似的步骤,煮沸水BoilWater()->冲泡Brew()->倒入杯中PourInCup()->加辅料AddCondiments(),这种不变的步骤便是模板方法定义中所说的“算法步骤”,步骤或算法不能改变,所以PrepareRecipe()方法无法被重写,但在Brew()和AddCondiments()阶段,咖啡和茶叶的具体操作又有不同,比如AddCondiments(),咖啡可能加的是牛奶,而茶叶加的可能是柠檬,所以算法中这两个方法要被具体的子类实现,进行针对性的操作。

另外,这儿还有个比较特殊的Hook钩子,钩子不是抽象方法,而是虚方法,它有自己的实现,但如果子类需要覆盖,也可以重写。这儿的例子中,用是否打包带走来举例,咖啡提供打包的服务,所以就重写了TakeOut()方法,但茶不允许外带,那么就留着父类中的TakeOut()空实现就好了。由此可见,钩子真是一个形象的名称。

那么,在模板方法模式中,如果算法的一个步骤必须被子类亲自实现,则设置为原语操作(PrimitiveOperation),c#可把这种方法设置为abstract抽象方法;如果并不是强制子类重写的步骤,则用钩子,c#可用virtual虚方法实现。

好莱坞原则

别调用我们,我们会调用你。

这个原则可以保证单向的依赖,避免出现高层组件与底层组件循环依赖的糟糕情况。

遵循好莱坞原则,可以让底层组件挂钩到高层系统上,高层组件会决定什么时候和怎样使用它们。

这和依赖倒置原则非常类似,但依赖倒置原则强调的是尽量避免使用具体类,而多使用抽象类;好莱坞原则更多是在教我们一个创建弹性设计的技巧,让底层组件能被挂钩进计算框架中,而且又不会让高层组件依赖低层组件。

与策略模式、工厂方法的比较

模板方法模式由子类决定如何实现算法中的步骤;策略模式封装可互换的行为,然后使用委托来决定要采用哪一个行为;工厂方法由子类决定实例化哪个具体类。

模板方法模式用继承进行算法实现,策略模式用了组合的方式;策略模式可以在运行时改变算法,但模板方法模式无法做到这一点。

最后,工厂方法是模板方法的一种特殊版本。

 

9 模板方法模式