• 模版方法模式


    一、   基本概述

    下面列出咖啡、茶的冲泡方法。

    1.咖啡冲泡方法

    (1)  把水煮沸

    (2)  用沸水冲泡咖啡

    (3)  把咖啡倒进杯子

    (4)  家牛奶和糖

    2.茶的冲泡方法

    (1)  把水煮沸

    (2)  用沸水浸泡茶叶

    (3)  把茶倒进杯子

    (4)  加柠檬

    在使用代码来完成这些方法时,我们一般想到的创建2个类(咖啡、茶类)来单独实现这四个步骤。或者更好点是创建一个饮料父类来共享第一步与第三步,然后继承父类实现各自饮料的第二步与第四步。那么还有更好一点的方式来处理上面的问题吗?有,可以使用模版方法模式。

    二、详细说明

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

    这个模式是用来创建一个算法的模版。什么是模版?如你所见,模版就是一个方法。更具体地说,这个方法将算法定义成一组步骤,其中的任何步骤都可以是抽象的,有子类负责实现。这可以确保算法的结构保持不变,同时由子类提供部分实现。

     

     

    钩子是一种被声明在抽象类中的方法,但只有空的或者默认的实现,钩子的存在,可以让子类有能力对算法的不同点进行挂钩。要不要挂钩,由子类自行决定。

     

    问:当我创建一个模版方法时,怎么才能知道什么时候该使用抽象方法,什么时候使用钩子呢?

    答:当你的子类“必须”提供算法中某个方法或步骤的实现时,就使用抽象方法。如果算法的这个部分是可选的,就用钩子。如果是钩子的话,子类可以选择实现这个钩子,但并不强制这么做。

    问:使用钩子真正的目的是什么?

    答:钩子有几种用法。钩子可以让子类实现算法中可选的部分,或者在钩子对于子类的实现并不重要的时候,子类可以对此钩子置之不理。钩子的另一个用法,是让子类能够有机会对模版方法中某些即将发生的步骤作出反应。钩子也可以让子类有能力为其抽象类作一些决定。

      如在Asp.net MVC框架中,你创建一个控制器,默认都是继承Controller类,而Controller类中就有钩子,如OnActionExecuted、OnActionExecuting、OnResultExecuted、OnResultExecuting等,这些钩子能够在你的控制器中进行实现,以便控制或加工对请求的动作与返回的执行。

    2.设计原则:好莱坞原则:别调用(打电话给)我们,我们会调用(打电话给)你。

        好莱坞原则可以给我们一种防止“依赖腐败”的方法。当高层组件依赖底层组件,而底层组件又依赖高层组件,而高层组件又依赖边侧组件,而边侧组件有依赖底层组件时,依赖腐败就发生了。在这种情况下,没有人可以轻易地搞懂系统是如何设计的。

        在好莱坞原则之下,我们允许底层组件将自己挂钩到系统上,但是高层组件会决定什么时候和怎样使用这些底层组件。换句话说,高层组件对待底层组件的方式是“别调用我们,我们会调用你”。

        好莱坞原则和模版方法之间的连接其实还算明显:当我们设计模版方法模式时,我们告诉子类,“不要调用我们,我们会调用你”。

     

    问:底层组件不可以调用高层组件中的方法吗?

    答:并不尽然,事实上,底层组件在结束时,常常会调用从超类中继承来的方法。我们所要做的是,避免让高层和底层组件之间有明显的环状依赖。

     

      模版方法模式是一个很常见的模式,到处都是。尽管如此,你必须拥有一双锐利的眼镜,因为模版方法有许多实现,而它们看起来并不一定和书上所说的设计一致。

      这个模式很常见是因为对创建框架来说,这个模式好用。有框架控制如何做事情,而由你(使用这个框架的人)指定框架算法中每个步骤的细节。

    如在C#数组中,有Array类提供一个Sort()方法用来排序,该方法有2个参数(一个是数组,一个是IComparer 接口),Sort方法提供了排序算法,实现IComparer 接口的参数提供了数组元素怎么进行比较大小。

     

    问:这真的是一个模版方法模式吗?

    答:我们都知道,荒野中的模式并非总是如同教科书例子一般地中规中矩,为了符合当前的环境和实现的约束,它们总是要被适当地修改。这个Array类的Sort()方法的设计者受到一些约束,通常我们无法设计一个类继承C#数组,而Sort()方法希望能够适用于所有的数组(每个数组都是不同的类)。所以它们定义了一个静态方法,而由被排序的对象内的每个元素自行提供比较大小的算法部分。所以,这虽然不是教科书上的模版方法,但它的实现仍然符合模版方法模式的精神。再者,由于不需要继承数组可以使用这个算法,这样使得排序变得更有弹性、更有用。

    4.总结:

    1.好莱坞原则告诉我们,将决策权放在高层模块中,以便决定如何以及何时调用底层模块。

    2.你将咋真实世界代码中看到模版方法模式的许多变体,不要期待它们全都是一眼就可以被你认出的。

    3.策略模式和模版方法模式都封装算法,一个用组合,一个用继承。

    4.工厂方法是模版方法的一种特殊版本。

    三、代码列表

    public abstract class CaffeineBeverage
    {
        public void PrepareRecipe()
        {
            BoilWater();
            Brew();
            PourInCup();
            AddCondiments();
            Hook();
        }
    
        protected abstract void Brew();
    
        protected abstract void AddCondiments();
    
        private void BoilWater()
        {
            Console.WriteLine("Boiling water");
        }
    
        private void PourInCup()
        {
            Console.WriteLine("Pouring into cup");
        }
    
        protected virtual void Hook()
        {
            
        }
    }
    public class Coffee:CaffeineBeverage
    {
        protected override void Brew()
        {
            Console.WriteLine("冲泡咖啡");
        }
    
        protected override void AddCondiments()
        {
        }
        
    }
    public class Tea : CaffeineBeverage
    {
        protected override void Brew()
        {
            Console.WriteLine("浸泡茶");
        }
    
        protected override void AddCondiments()
        {
            Console.WriteLine("加柠檬");
        }
    }
    View Code

    ---------以上内容根据《Head First Design Mode》进行整理

  • 相关阅读:
    多路复用与设置阻塞、非阻塞模式
    ['\xef\xbb\xbf这个什么含义? PY技术开发交流区 乐讯手机高手
    fcntl使用 and_tt 博客园
    Linux 设备驱动 Edition 3Linux设备驱动第三版(中文版)
    CRT source Google 搜索
    BOM–字节序标记 永不放弃的地盘 博客频道 CSDN.NET
    在C语言中,unsigned char是什么类型?_百度知道
    The JR Concurrent Programming Language
    C语言:为什么用fprintf(stderr,"Error");比printf("Error");更好?
    bash
  • 原文地址:https://www.cnblogs.com/zwt-blog/p/7003375.html
Copyright © 2020-2023  润新知