模板方法
模板方法是一个方法,定义了算法的步骤,并允许子类为一个或多个步骤提供实现。
本例中用冲泡咖啡和茶的例子来说明:
上图说明了冲泡咖啡和茶的步骤,可以看出冲泡咖啡和茶的步骤差不多,很相似,先来看看没有应用模板方法的代码:
Tea的代码类似,就不放了。他们的一个不好的地方在于算法散落在了各个类中,还有一点是重复的代码。
public abstract class CaffeineBeverage//设计一个抽象类,将算法进行封装 { public void BoilWater() { Console.WriteLine("boil water"); } public void PourInCup() { Console.WriteLine("pour in cup"); } public void PrepareRecipe()//封装算法,这个就是模板方法。 { BoilWater(); Brew(); PourInCup(); AddCondiment(); } public abstract void Brew();//具体的实现要到子类中实现 public abstract void AddCondiment();//具体的实现要到子类中实现 } public class TeaLogic: CaffeineBeverage { public override void Brew() { Console.WriteLine("brew tea"); } public override void AddCondiment() { Console.WriteLine("add some lemon"); } } public class CoffeLogin:CaffeineBeverage { public override void Brew() { Console.WriteLine("brew coffe..."); } public override void AddCondiment() { Console.WriteLine("add some milk and sugar..."); } }
模板方法定义了一个算法步骤,并允许子类其中一个或多个实现提供步骤。
模板方法的定义:在一个方法中定义算法的骨架,而将一些步骤延迟到子类中。模板方法可以在子类不改变算法结构的情况下,重新定义算法中的某些步骤。
同时,AbstractClass内部定义一个钩子方法——一个虚方法,可以在子类中决定是否重写,达到挂钩的目的。钩子的存在,可以让子类有能力对算法的不同点进行挂钩,要不要挂钩,由子类自行决定。
//在模板方法中应用钩子 public abstract class AnotherKindOfCoffeineBeverage { public void BoilWater() { Console.WriteLine("boil water"); } public void PourInCup() { Console.WriteLine("pour in cup.."); } public virtual bool CustomerWantCondiment()//虚方法。。表示一个钩子 { return true; } public abstract void Brew(); public abstract void AddCondiment(); public void PrepareRecipe() { BoilWater(); Brew(); if (CustomerWantCondiment()) { AddCondiment(); } PourInCup(); } } //应用钩子方法的一个场景 public class AnotherKindOfTea : AnotherKindOfCoffeineBeverage { public override bool CustomerWantCondiment() { return GetCustomerInput().Equals("y"); } public override void Brew() { Console.WriteLine("dropping coffe through filter.."); } public override void AddCondiment() { Console.WriteLine("add some condiment.."); } private string GetCustomerInput() { Console.WriteLine("would you like some lemon with your tea?(y/n)"); var input = Console.ReadLine(); return input?.ToLower(); } }
关于钩子,由这么一个原则:当你必须提供模板方法中的某个步骤时,就在基类中做成抽象方法,如果这个步骤是可选的,就做成虚方法,钩子。
钩子可以让子类实现算法中的可选部分,或者在钩子对于子类的实现不那么重要的时候,子类可以对此钩子置之不理。钩子的另一个用法,是让子类能够有机会对模板方法中某些即将发生的步骤做出反应。钩子也可以让子类有能力为其抽象类做一些决定。
最重要的一点:抽象类中的某些方法是可选的,对于这些可选的方法,就做成钩子,而不是做成抽象方法,这样就可以让子类的负荷减轻。
新的设计原则:好莱坞设计原则——不要调用(打电话给)我们,我会调用(打电话给)你。
好莱坞原则和模板方法
他们两个的联系还算是比较明显:当我们设计模板方法时,我们告诉子类,“不要调用我们,我们会调用你”。
好莱坞原则和依赖倒置原则之间的关系: