深入浅出设计模式-009:模板方法模式(Template Method Pattern)
一:如泡茶喝泡咖啡,步骤都一样,四部 class Coffee{ public void prepareRecipe(){ boilWater(); brewCoffeeGrinds(); pourInCup(); addSugarAndMilk(); } public void boilWater(){ Console.WriteLine("boilWater"); } public void brewCoffeeGrinds(){ Console.WriteLine("brewCoffeeGrinds"); } public void pourInCup(){ Console.WriteLine("pourInCup"); } public void addSugarAndMilk(){ Console.WriteLine("addSugarAndMilk"); } } class Tea{ public void prepareRecipe(){ boilWater(); steepTeaBag(); pourInCup(); addLenmon(); } public void boilWater(){ Console.WriteLine("boilWater"); } public void steepTeaBag(){ Console.WriteLine("brewCoffeeGrinds"); } public void pourInCup(){ Console.WriteLine("pourInCup"); } public void addLenmon(){ Console.WriteLine("addSugarAndMilk"); } }
二:第一步和第三步是一样的,可以抽象出来 abstract class CaffeineBeverage{ public abstract void prepareRecipe();
public void boilWater(){ Console.WriteLine("boilWater"); } public void pourInCup(){ Console.WriteLine("pourInCup"); } } class Coffee : CaffeineBeverage{ public override void prepareRecipe(){ boilWater(); brewCoffeeGrinds(); pourInCup(); addSugarAndMilk(); } public void brewCoffeeGrinds(){ Console.WriteLine("brewCoffeeGrinds"); } public void addSugarAndMilk(){ Console.WriteLine("addSugarAndMilk"); } } class Tea : CaffeineBeverage{ public override void prepareRecipe(){ boilWater(); steepTeaBag(); pourInCup(); addLenmon(); } public void steepTeaBag(){ Console.WriteLine("brewCoffeeGrinds"); } public void addLenmon(){ Console.WriteLine("addSugarAndMilk"); } }
三:将第二步和第四步统一命名,这样就可以将prepareRecipe提到抽象类中 abstract class CaffeineBeverage{ //模板方法,定义步骤 public void prepareRecipe(){ boilWater(); brew(); pourInCup(); addConditiments(); }
public abstract void brew(); public abstract void addConditiments();
public void boilWater(){ Console.WriteLine("boilWater"); } public void pourInCup(){ Console.WriteLine("pourInCup"); } } class Coffee : CaffeineBeverage{ public override void brew(){ Console.WriteLine("brewCoffeeGrinds"); } public override void addConditiments(){ Console.WriteLine("addSugarAndMilk"); } } class Tea : CaffeineBeverage{ public override void brew(){ Console.WriteLine("brewCoffeeGrinds"); } public override void addConditiments(){ Console.WriteLine("addSugarAndMilk"); } }
四:模板方法模式:在一个方法中定义一个算法的骨架,而将一些步骤延迟到子类中。 模板方法使得子类可以在不改变算法结构的情况下,重新定义算法中的某些步骤。 abstract class AbstractClass { void templateMethod() { primitiveOperation1(); primitiveOperation2(); concreateOperation(); } abstract void primitiveOperation1(); abstract void primitiveOperation2(); void concreateOperation() { 添加实现}; }
五:引入钩子 1: 钩子是一种被声明在抽象类中的方法,但是有空的或者默认的实现。 钩子的存在可疑让子类有能力对算法的不同点进行挂钩。要不要挂钩,由子类决定 abstract class AbstractClass { void templateMethod() { primitiveOperation1(); primitiveOperation2(); concreateOperation(); hook(); } abstract void primitiveOperation1(); abstract void primitiveOperation2(); void concreateOperation() { 添加实现} void hook(){} } 2: 当子类“必须”提供算法中某个方法或步骤时,就是用抽象方法。 如果算法的这个部分是可选的,就用钩子。 如果是钩子的话,子类就可以选择实现这个钩子,但是并不强制这么做,应为提供默认实现了。 3: 钩子的作用 钩子可以让子类实现算法中的可选部分。 当钩子对于子类的实现并不重要的时候,子类可以对钩子置之不理。 子类能够有激活对模板方法中的某些即将发生的步骤做出反应。
六:钩子的应用 abstract class CaffeineBeverage{ public void prepareRecipe(){ boilWater(); brew(); pourInCup(); if (customerWantsCondiments()){ addConditiments(); } } public abstract void brew(); public abstract void addConditiments(); public void boilWater(){ Console.WriteLine("boilWater"); } public void pourInCup(){ Console.WriteLine("pourInCup"); } //定义空的缺省实现 //子类可以覆盖这个方法,但不一定见得要这么做 public virtual Boolean customerWantsCondiments(){ return true; } } class Coffee : CaffeineBeverage{ public override void brew(){ Console.WriteLine("brewCoffeeGrinds"); } public override void addConditiments(){ Console.WriteLine("addSugarAndMilk"); } public override bool customerWantsCondiments(){ //用户自行控制是否加入甜点 //此处可以做成用户按键输入的 return false; } } class Tea : CaffeineBeverage{ public override void brew(){ Console.WriteLine("brewCoffeeGrinds"); } public override void addConditiments(){ Console.WriteLine("addSugarAndMilk"); } public override bool customerWantsCondiments(){ //用户自行控制是否加入甜点 //此处可以做成用户按键输入的 return true; } }
七:设计原则:好莱坞原则:别调用(打电话给)我们,我们会调用(打电话给)你。 即允许低层组件将自己挂钩到系统上,但是高层组件会决定什么时候和怎样使用这些顶层组件。