zoukankan      html  css  js  c++  java
  • 设计模式(二十二)模板方法模式 Template

    • 泡茶?泡咖啡?

      我们用泡茶和泡咖啡两种行为来引入这一设计模式。

      思考一下“泡茶”的过程:

      煮水 -> 用沸水泡茶叶 -> 把茶倒进杯子 -> 放点柠檬之类的佐料。

      然后再看一下“泡咖啡”的过程:

      煮水 -> 用沸水泡咖啡 -> 把咖啡倒进杯子 -> 加牛奶和糖。

      如果我们用两个类去描述这两个过程,很明显会有很多重复的代码(例如 Step1 煮水,Step3 倒进杯子),也有很多相似的代码(Step2 冲泡,Step4 加佐料)。

      将冲泡的过程看做是一个算法,那么这个算法的主架构是一致的:

      煮水 -> 用沸水泡东西 -> 将泡完的东西倒进杯子 -> 加佐料。

      将主过程抽象出来,至于泡什么东西,加什么佐料,就交给子类去实现,这就是模板方法模式。

    • 模板方法模式:

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

    • UML:

      除了上述的四个步骤,这里加了个新的方法:customerWantsCondiments(),因为加佐料这个步骤不是必须的。

    • 模板方法的组成:

      从上面 UML 图,我们能够发现,在抽象类 BeverageDrive 中,一共存在4类方法:

    1. 模板方法:prepareRecipe(),这是算法的主体,在方法内部会调用其他的方法,一般来说是 public final 的。
    2. 抽象方法:brew() & addCondiments(),是算法步骤的“个性”,在 BeverageDrive 中声明为 protected abstract,由子类去实现。
    3. 具体方法:boilWater() & pourInCup(),是算法步骤的“共性”,一般是 private 的。
    4. 钩子方法:customerWantsCondiments,具有空的或是默认的实现。钩子的存在,可以让子类有能力对算法的不同点进行挂钩,选择权在子类手上,一般是 protected 的。
    • 代码:
    public abstract class BeverageDrive {
    
        // Template method
        public final void prepareRecipe() {
            boilWater();
            brew();
            pourInCup();
            if (customerWantsCondiments()) {
                addCondiments();
            }
        }
    
        // Abstract method
        protected abstract void brew();
    
        protected abstract void addCondiments();
    
        // Concrete method
        private void boilWater() {
            System.out.println("Boiling water");
        }
    
        private void pourInCup() {
            System.out.println("Pouring into cup");
        }
    
        // Hook method
        protected boolean customerWantsCondiments() {
            return true;
        }
    }
    public final class CoffeeBeverage extends BeverageDrive {
    
        @Override
        public void brew() {
            System.out.println("Dripping coffee through filter");
        }
    
        @Override
        public void addCondiments() {
            System.out.println("Adding sugar and milk");
        }
    
        @Override
        public boolean customerWantsCondiments() {
            return false;
        }
    }
    public final class TeaBeverage extends BeverageDrive {
    
        @Override
        public void brew() {
            System.out.println("Steeping the tea");
        }
    
        @Override
        public void addCondiments() {
            System.out.println("Adding lemon");
        }
    }
    • 好莱坞原则:

      好莱坞在寻找演员的时候有一个著名的原则:别打电话给我们,我们会打电话给你。

      这一点在 OO 设计上,被翻译为:别调用我,我会调用你。

      在好莱坞,演员属于低层组件,电影公司属于高层组件。每当电影公司需要演员的时候,都是电影公司打电话通知演员来面试。

      换句话说:高层组件对待低层组件的方式是:别调用我,我会调用你。

      当我们设计模板方法模式的时候,要时刻记得,让父类去调用子类,不要让子类调用父类方法。

      下面看一个不太好的设计:

    public abstract class BadBehaviorBeverageDrive {
    
        protected void boilWater() {
            System.out.println("Boiling water");
        }
    
        protected void pourInCup() {
            System.out.println("Pouring into cup");
        }
    
        protected boolean customerWantsCondiments() {
            return true;
        }
    }
    public final class BadBehaviorTeaBeverage extends BadBehaviorBeverageDrive {
    
        public final void prepareRecipe() {
            super.boilWater();
            steepTeaBags();
            super.pourInCup();
            if (super.customerWantsCondiments()) {
                addLemon();
            }
        }
    
        private void steepTeaBags() {
            System.out.println("Steeping the tea");
        }
    
        private void addLemon() {
            System.out.println("Adding lemon");
        }
    }

      这个设计,一定意义上来说,达到了代码重用的功能,但是违反了好莱坞原则,不能算是一个合格的设计。

  • 相关阅读:
    python 展开嵌套列表
    python对字典排序
    CentOS7 Network Setting
    华为交换机Stelnet ssh/rsa验证模式下16进制公钥生成方法
    CentOS7 DHCP自动获取IP地址
    拔掉网线才能登陆域
    Exchange日志清理
    Exchange日志
    EMS邮箱数据库常用命令(二)
    EMS邮箱数据库常用命令(一)
  • 原文地址:https://www.cnblogs.com/jing-an-feng-shao/p/10945864.html
Copyright © 2011-2022 走看看