zoukankan      html  css  js  c++  java
  • 八、模板方法模式(Template Method Pattern)《HeadFirst设计模式》读书笔记

      在抽象类中创建一个模板方法,这个方法可以调用在抽象类中定义好的其它方法,这些方法可以是抽象的,也可以是默认方法,甚至还可以是一个空的方法(叫做钩子),可以在子类中重写抽象方法或重写钩子方法,从而实现模板方法的某些具体步骤,这就是模板方法模式。模板方法模式可以实现代码的复用。

      让我们来看一个具体的例子:

      咖啡和柠檬茶的制作过程都要经过四个步骤:

        1.烧开水;

        2.冲咖啡/泡茶叶;

        3.倒入杯中;

        4.加糖和牛奶/加柠檬。

      可以看到,四个步骤中第1步和第3步其实是一样的,如果把上面的过程分别写进咖啡类和柠檬茶类中的话,那每个类中会有两个方法是重复的,如何实现方法的复用呢?

      经过了一些设计模式的学习,我们发现很多设计模式都是通过组合和继承来实现的,这里说说我自己的想法:

        1.如果通过组合的方式,可以像命令模式那样把不同的操作步骤封装成一个对象中的方法,然后在咖啡类和柠檬茶类中分别组合这些对象,这样每个步骤都可以被复用;

        2.如果通过继承的方式,可以抽象出一个父类,在父类中将相同的步骤1和3写成默认方法,将不同的步骤写成抽象方法,让咖啡和柠檬茶继承这个父类,并分别提供自己对抽象方法的重写。

      上面说的第二种方式其实就是模板方法模式的标准形式,来看一下具体代码实现:

    public abstract class Father {
        //模板方法,不希望被改变(被子类覆盖),所以定义成final的
        public final void templateMethod(){
            boilWater();
            brew();
            pourIntoCup();
            addOther();
        }
        //步骤1
        public void boilWater(){
            System.out.println("烧开水");
        }
        //步骤2
        public abstract void brew();
        //步骤3
        public void pourIntoCup(){
            System.out.println("倒入杯中");
        }
        //步骤4
        public abstract void addOther();
    }
    
    public class Coffee extends Father {
        //在咖啡类中重写父类中的抽象方法
        @Override
        public void brew() {
            System.out.println("冲咖啡");
        }
        @Override
        public void addOther() {
            System.out.println("加糖和牛奶");
        }
    }
    
    public class LemonTea extends Father {
        //在柠檬茶类中重写父类中的抽象方法
        @Override
        public void brew() {
            System.out.println("泡茶");
        }
        @Override
        public void addOther() {
            System.out.println("加柠檬");
        }
    }
    
    //测试
    public class test01 {
        public static void main(String[] args) {
            Coffee coffee = new Coffee();
            LemonTea lemonTea = new LemonTea();
            coffee.templateMethod();
            lemonTea.templateMethod();
        }
    }

      测试结果:

       注意到如果不希望模板方法被覆盖,可以声明成final。模板方法定义好了整个方法的框架,有些具体的部分可以在子类中去实现。

      另外上面提到的钩子又是怎么一回事呢,我们改造一下上面的例子,在模板方法中加入一个hook()方法:

    public abstract class Father {
        //模板方法,不希望被改变(被子类覆盖),所以定义成final的
        public final void templateMethod(){
            boilWater();
            brew();
            pourIntoCup();
            addOther();
            hook();
        }
    
        private void hook() {
            //什么都不做,由子类决定是否覆盖
        }
    
        //步骤1
        public void boilWater(){
            System.out.println("烧开水");
        }
        //步骤2
        public abstract void brew();
        //步骤3
        public void pourIntoCup(){
            System.out.println("倒入杯中");
        }
        //步骤4
        public abstract void addOther();
    }

      可以看到这个hook()方法并不是抽象方法,也就是子类覆不覆盖都可以,这样可以更灵活的附加一些功能。另外还可以做一些条件判断来控制模板方法内一些方法的执行,比如:

    public abstract class Father {
        //模板方法,不希望被改变(被子类覆盖),所以定义成final的
        public final void templateMethod(){
            boilWater();
            brew();
            pourIntoCup();
            //是否要加额外的配料
            if (wannaAdd()) {
                addOther();
            }
        }
        //默认返回true
        private boolean wannaAdd() {
            return true;
        }
    
        //步骤1
        public void boilWater(){
            System.out.println("烧开水");
        }
        //步骤2
        public abstract void brew();
        //步骤3
        public void pourIntoCup(){
            System.out.println("倒入杯中");
        }
        //步骤4
        public abstract void addOther();
    }

      上面的案例默认返回true,也就是要加配料,子类可以覆盖这个方法返回false,还可以让用户输入是否要加配料来返回true/false,钩子为模板方法提供了更多的灵活性。

      另外上面的例子是模板方法的标准形式,那么不标准的呢,下面说一个实际的例子。在JDK1.8中的Arrays类中的静态方法sort(Object[] o),它可以对数组中的元素进行排序,在sort(Object[] o)方法中调用了一个legacyMergeSort()方法,这个方法内又调用了mergeSort()方法,这是一个模板方法,在这个方法中自己实现了一部分,另外抽象的方法是通过将传递进来的数组元素强转成Comparable接口调用的compareTo()方法,因此数组元素要实现Comparable接口并重写compareTo()方法。可见这个模板方法的实现和上面的标准形式稍微有些不同,因为数组的特殊性,JDK在Arrays工具类写了一些处理数组的方法,比如sort(),在内部的模板方法内,通过数组元素实现Comparable接口并重写compareTo()方法,实现将具体实现延迟到子类(数组元素)中,由这种表述方式也可以知道其实工厂方法也是一种特殊的模板方法,只不过它是用来创建对象的。

      总结:

        1.模板方法定义了算法的步骤,一些步骤可以在子类中实现,实现了代码的复用,还可以通过钩子实现更灵活的控制;

        2.如果不想让子类改变模板方法,可以将模板方法定义成final的

        3.模板方法也有一些衍生情况,有时并不一定是在模板方法所在类的子类中去实现方法,比如Arrays工具类,就在模板方法中调用了Comparable接口的抽象方法compareTo(),具体实现是在数组元素中重写的。

        4.工厂方法其实也可以看成是模板方法的一种特殊版本,父类中的创建对象的抽象方法就可以看成模板方法,在子类中去返回具体的对象。

        5.模板方法的特殊情况其实有点类似于通过组合来实现的,只不过传递进来参数不是接口而是具体实现然后再强转成接口,如果说是组合的话那就有点类似于策略模式,不同的是策略模式通常通过组合实现了算法的全部,而模板方法只是实现了模板中的一部分。

  • 相关阅读:
    宁泽涛世锦赛夺冠
    iOS手势识别的详细使用(拖动,缩放,旋转,点击,手势依赖,自定义手势)
    在xcode里面如何利用instrument
    Xcode 中搜索任何条件文本
    十大算法
    Object c中的alloc和init问题
    My97DaePicker 用js实现文本框日期相减求天数
    js 取消listbox选中的项
    C# OR/Mapping 数据处理模式学习
    ASP.NET实现列表页连接查询 拼接sql语句 绑定grivdView
  • 原文地址:https://www.cnblogs.com/advancedcz/p/13295641.html
Copyright © 2011-2022 走看看