关闭

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

标签: 模板方法模板方法模式
151人阅读 评论(0) 收藏 举报
分类:


理解

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

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


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

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

查看评论
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
    个人资料
    • 访问:123941次
    • 积分:2723
    • 等级:
    • 排名:第13197名
    • 原创:135篇
    • 转载:135篇
    • 译文:0篇
    • 评论:20条
    最新评论