23种设计模式 第三部分 关系模式(2)模板方法模式

来源:互联网 发布:网络信息安全认证 编辑:程序博客网 时间:2024/06/05 18:59


理解

模板方法定义了一个算法的步骤,并允许子类为一个或多个步骤提供实现。

泡茶和泡咖啡,步骤很相似:


两种饮品其分别的做法如下代码:

public class Coffee {     public void PrepareRecipe()     {         //烧水         BoilWater();         //冲咖啡         BrewCoffeeGrinds();         //倒入茶杯中         PourInCup();         //加入糖和咖啡         AddSugarAndMilk();     }     public void BoilWater()     {         Console.WriteLine("烧水");     }    public void BrewCoffeeGrinds()     {         Console.WriteLine("冲咖啡");     }    public void PourInCup()     {         Console.WriteLine("倒入杯子中");     }    public void AddSugarAndMilk()     {         Console.WriteLine("加糖和牛奶");     } }public class Tea {     public void PrepareRecipe()     {         //烧水         BoilWater();         //泡茶         SteepTeaBag();         //倒入茶杯中         PourInCup();         //加入柠檬         AddLemon();     }     public void BoilWater()     {         Console.WriteLine("烧水");     }    public void SteepTeaBag()     {         Console.WriteLine("泡茶");     }    public void PourInCup()     {         Console.WriteLine("倒入杯子中");     }    public void AddLemon()     {         Console.WriteLine("加柠檬");     } }
烧水和带入杯子的方法显然是重复的,这样就不符合代码复用。 
对比两种做法,都是需要四个步骤,把相同的使用一个基类,不同的部分分别由自己的子类去实现。

冲咖啡和泡茶以及加入咖啡和牛奶都是属于差不多动作相同的。所以可以继续抽象,抽象为“泡”和“加调料”。抽象后的方法大致如此:




得出的也就是我们的模板方法。下面来看看模板方法模式的类图:


实现

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();      }  }

输出:


已经去掉了调料。

模板方法模式还涉及到一个好莱坞原则:

别调用(打电话给)我们,我们会调用(打电话给)你。

在模板方法模式中扮演好莱坞的角色是抽象类,子类是演员的角色。

我们允许低层组件将自己挂钩到系统上,但是高层组件会决定什么时候和怎样使用这些低层组件。换句话说,高层组件对待低层组件的方式是“别调用我们,我们会调用你。”我们要做的事,避免让高层和低层组件之间有明显的环状依赖。

总结

对比模板方法模式、策略模式以及工厂方法模式:

模板方法  - -> 封装可互换的行为,然后使用委托来决定要采用哪一个行为。

策略  -- >子类决定如何实现算法中的步骤。

工厂方法  -- > 由子类决定实例化哪个具体类。



0 0