• 《Head First 设计模式》:模板方法模式


    正文

    一、定义

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

    要点:

    • 模板方法定义了一个算法的步骤,每个步骤都被一个方法所代表,而这几个方法的具体实现可由子类提供。
    • 模板方法可确保算法的结构保持不变,同时由子类提供部分实现。

    二、实现步骤

    1、创建一个抽象类,并定义模板方法

    模板方法一般声明为 final,以免子类改变算法的步骤。
    抽象类中,可以声明一些钩子方法,子类视情况决定要不要覆盖它们。钩子的存在,可以让子类有能力对算法的不同点进行挂钩,使得模板方法更具有弹性。

    /**
     * 抽象类
     */
    public abstract class AbstractClass {
        
        /**
         * 模板方法
         */
        public final void templateMethod() {
            // 公共步骤,由抽象类实现
            commonStep();
            // 依赖于子类的步骤,由子类实现
            step1();
            step2();
            step3();
            // 钩子方法,由子类决定要不要覆盖
            hook();
        }
        
        /**
         * 步骤1
         */
        public abstract void step1();
        
        /**
         * 步骤2
         */
        public abstract void step2();
        
        /**
         * 步骤3
         */
        public abstract void step3();
        
        /**
         * 公共步骤
         */
        private void commonStep() {
            System.out.println("I'm common step!");
        }
        
        /**
         * 钩子方法
         */
        public void hook() {
            // 可以空实现,也可以提供默认实现
        }
    }
    

    2、创建具体子类,并提供算法步骤的具体实现

    (1)具体子类A

    /**
     * 具体子类A
     */
    public class ConcreteClassA extends AbstractClass{
    
        @Override
        public void step1() {
            System.out.println("I'm step A1!");
        }
    
        @Override
        public void step2() {
            System.out.println("I'm step A2!");
        }
    
        @Override
        public void step3() {
            System.out.println("I'm step A3!");
        }
        
        @Override
        public void hook() {
            System.out.println("I'm hook step A!");
        }
    }
    

    (2)具体子类B

    /**
     * 具体子类B
     */
    public class ConcreteClassB extends AbstractClass{
    
        @Override
        public void step1() {
            System.out.println("I'm step B1!");
        }
    
        @Override
        public void step2() {
            System.out.println("I'm step B2!");
        }
    
        @Override
        public void step3() {
            System.out.println("I'm step B3!");
        }
    }
    

    3、通过使用不同的子类,来确定具体的算法步骤

    public class Test {
        
        public static void main(String[] args) {
            AbstractClass classA = new ConcreteClassA();
            classA.templateMethod();
            System.out.println();
            AbstractClass classB = new ConcreteClassB();
            classB.templateMethod();
        }
    }
    

    三、举个栗子

    1、背景

    《星巴兹咖啡师傅训练手册》规定了,准备星巴兹饮料时,必须精确地遵循下面的冲泡法:

    星巴兹咖啡冲泡法:

    1. 把水煮沸
    2. 用沸水冲泡咖啡
    3. 把咖啡倒进杯子
    4. 加糖和牛奶

    星巴兹茶冲泡法:

    1. 把水煮沸
    2. 用沸水浸泡茶叶
    3. 把茶倒进杯子
    4. 加柠檬

    现在,让我们扮演“代码师傅”,写一些代码来创建咖啡和茶。

    2、实现

    (1)创建咖啡因饮料抽象类,并定义冲泡步骤的方法

    /**
     * 咖啡因饮料抽象类
     */
    public abstract class CaffeineBeverage {
        
        /**
         * 准备冲泡法(模板方法)
         */
        final void prepareRecipe() {
            boilWater();
            brew();
            pourInCup();
            if (customerWantsCondiments()) {
                addCondiments();
            }
        }
        
        /**
         * 冲泡(依赖于子类的步骤)
         */
        abstract void brew();
        
        /**
         * 添加调料(依赖于子类的步骤)
         */
        abstract void addCondiments();
        
        /**
         * 烧水(公共步骤)
         */
        void boilWater() {
            System.out.println("Boiling water");
        }
        
        /**
         * 把饮料倒进杯子(公共步骤)
         */
        void pourInCup() {
            System.out.println("Pouring into cup");
        }
        
        /**
         * 顾客是否想要添加调料(钩子方法)
         */
        boolean customerWantsCondiments() {
            return true;
        }
    }
    

    (2)创建具体的饮料,并提供具体冲泡步骤的实现

    /**
     * 咖啡
     */
    public class Caffee extends CaffeineBeverage{
    
        @Override
        void brew() {
            System.out.println("Dripping Coffee through filter");
        }
    
        @Override
        void addCondiments() {
            System.out.println("Adding Sugar and Milk");
        }
        
        @Override
        boolean customerWantsCondiments() {
            String answer = askCustomer();
            if (answer.toLowerCase().startsWith("y")) {
                return true;
            } else {
                return false;
            }
        }
        
        /**
         * 询问顾客
         */
        private String askCustomer() {
            System.out.print("Would you like milk and suger with your coffee (y/n)?");
            String answer = null;
            Scanner scanner = new Scanner(System.in);
            if (scanner.hasNext()) {
                answer = scanner.nextLine();
            }
            scanner.close();
            if (answer == null) {
                return "no";
            }
            return answer;
        }
    }
    
    /**
     * 茶
     */
    public class Tea extends CaffeineBeverage{
    
        @Override
        void brew() {
            System.out.println("Steeping the tea");
        }
    
        @Override
        void addCondiments() {
            System.out.println("Adding Lemon");
        }
    }
    

    (3)冲泡饮料

    public class Test {
        
        public static void main(String[] args) {
            // 茶
            System.out.println("
    Making tea...");
            CaffeineBeverage tea = new Tea();
            tea.prepareRecipe();
            // 咖啡
            System.out.println("
    Making coffee...");
            CaffeineBeverage caffee = new Caffee();
            caffee.prepareRecipe();
        }
    }
    
  • 相关阅读:
    Android内存优化杂谈
    ANDROID内存优化(大汇总——全)
    ANDROID内存优化(大汇总——中)
    ANDROID内存优化(大汇总——上)
    Java反射学习总结终(使用反射和注解模拟JUnit单元测试框架)
    Java反射学习总结五(Annotation(注解)-基础篇)
    Java反射学习总结四(动态代理使用实例和内部原理解析)
    Java反射学习总结三(静态代理)
    Java反射学习总结二(用反射调用对象的私有属性和方法)
    Java反射学习总结一(基础篇)
  • 原文地址:https://www.cnblogs.com/jingqueyimu/p/13586886.html
Copyright © 2020-2023  润新知