zoukankan      html  css  js  c++  java
  • 设计模式--模板方法模式

    模板方法模式

    业务流程固定,只是具体某些步骤存在可变化的部分。如银行办理业务流程

    银行办理业务流程:

    Step 1、进门取号

    Step 2、填写单据

    Step 3、等待叫号

    Step 4、窗口办理

    其中Step 2业务随着办理的业务不同,填写的单据不同,但是其他3个步骤都是相同的。

    例子:饮料机可以冲泡咖啡和茶叶

    提神饮料泡法:

    1)把水煮沸(boilWater)

    2)泡饮料(brew)

    3)把饮料倒进杯子(pourInCup)

    4)加入调味料(addCondiments)

    冲咖啡和冲茶叶的流程是相同的,只是步骤2和步骤4分别不同,所以可以使用模板方法模式进行设计和实现。

    1、抽象基类定义框架

    package com.templatemehod;
    //模版方法模式
    //抽象基类,为所有子类提供一个算法框架
    //提神饮料
    public abstract class RefreshBeverage {
        //制备饮料的模版方法
        //封装了所有子类共同遵循的算法框架
        public final void prepareBeverageTemplate(){
            //步骤1 将水煮沸
            boilWater();
            //步骤2 泡制饮料
            brew();
            //步骤3 将饮料倒入杯中
            pourInCup();
            //步骤4 加入调味料
            addCondiments();
        }
    
        private void boilWater() {
            System.out.println("1、将水煮沸");
        }
    
        private void pourInCup() {
            System.out.println("3、将饮料倒入杯中");
        }
    
        protected abstract void brew();
    
        protected abstract void addCondiments();
        
    }

    2、具体子类实现延迟步骤

    package com.templatemehod;
    //具体子类,提供了咖啡制备的具体实现
    public class Coffee extends RefreshBeverage {
    
        @Override
        protected void brew() {
            System.out.println("2、用沸水冲泡咖啡");
        }
    
        @Override
        protected void addCondiments() {
            System.out.println("4、加入糖和牛奶");
        }
    
    }
    package com.templatemehod;
    //具体子类,提供了制备茶的具体实现
    public class Tea extends RefreshBeverage {
    
        @Override
        protected void brew() {
            System.out.println("2、用80度热水浸泡茶叶5分钟");
        }
    
        @Override
        protected void addCondiments() {
            System.out.println("4、加入柠檬");
        }
    }

    3、测试一下

    package com.templatemehod;
    
    public class RefreshBeverageTest {
    
        public static void main(String[] args) {
            System.out.println("制备咖啡...");
            RefreshBeverage b1 = new Coffee();
            b1.prepareBeverageTemplate();
            System.out.println("咖啡好了...");
            
            System.out.println("
    ...........................");
            System.out.println("制备茶...");
            RefreshBeverage b2 = new Tea();
            b2.prepareBeverageTemplate();
            System.out.println("茶好了...");
        }
    }

    测试结果:

    制备咖啡...
    1、将水煮沸
    2、用沸水冲泡咖啡
    3、将饮料倒入杯中
    4、加入糖和牛奶
    咖啡好了...

    ...........................
    制备茶...
    1、将水煮沸
    2、用80度热水浸泡茶叶5分钟
    3、将饮料倒入杯中
    4、加入柠檬
    茶好了...

    4、钩子使子类更灵活

    如:冲茶叶的时候不想加入调料,冲咖啡的时候还是要加入糖和牛奶

    1、抽象基类定义框架

    package com.templatemehod;
    //模版方法模式
    //抽象基类,为所有子类提供一个算法框架
    //提神饮料
    public abstract class RefreshBeverage {
        //制备饮料的模版方法
        //封装了所有子类共同遵循的算法框架
        public final void prepareBeverageTemplate(){
            //步骤1 将水煮沸
            boilWater();
            //步骤2 泡制饮料
            brew();
            //步骤3 将饮料倒入杯中
            pourInCup();
            //步骤4 加入调味料
            if(isCustomerWantsCondiments()){
                addCondiments();
            }
        }
        
        //Hook,钩子函数,提供一个默认或空的实现
        //具体的子类可以自行决定是否挂钩以及如何挂钩
        //询问用户是否加入调料
        protected boolean isCustomerWantsCondiments() {
            return true;
        }
    
        private void boilWater() {
            System.out.println("1、将水煮沸");
        }
    
        private void pourInCup() {
            System.out.println("3、将饮料倒入杯中");
        }
    
        protected abstract void brew();
        
        protected abstract void addCondiments();
    
    }

    2、具体子类实现延迟步骤

    package com.templatemehod;
    //具体子类,提供了咖啡制备的具体实现
    public class Coffee extends RefreshBeverage {
    
        @Override
        protected void brew() {
            System.out.println("2、用沸水冲泡咖啡");
        }
    
        @Override
        protected void addCondiments() {
            System.out.println("4、加入糖和牛奶");
        }
    
    }
    package com.templatemehod;
    //具体子类,提供了制备茶的具体实现
    public class Tea extends RefreshBeverage {
    
        @Override
        protected void brew() {
            System.out.println("2、用80度热水浸泡茶叶5分钟");
        }
    
        @Override
        protected void addCondiments() {
            System.out.println("4、加入柠檬");
        }
        
        //自定义是否需要挂钩
        @Override
        protected boolean isCustomerWantsCondiments() {
            return false;
        }
    }

    3、测试一下

    package com.templatemehod;
    
    public class RefreshBeverageTest {
    
        public static void main(String[] args) {
            System.out.println("制备咖啡...");
            RefreshBeverage b1 = new Coffee();
            b1.prepareBeverageTemplate();
            System.out.println("咖啡好了...");
            
            System.out.println("
    ...........................");
            System.out.println("制备茶...");
            RefreshBeverage b2 = new Tea();
            b2.prepareBeverageTemplate();
            System.out.println("茶好了...");
        }
    
    }

    测试结果: 在泡茶的时候,不加入调料

    制备咖啡...
    1、将水煮沸
    2、用沸水冲泡咖啡
    3、将饮料倒入杯中
    4、加入糖和牛奶
    咖啡好了...

    ...........................
    制备茶...
    1、将水煮沸
    2、用80度热水浸泡茶叶5分钟
    3、将饮料倒入杯中
    茶好了...

    模板方法模式总结

    模板方法模式的实现要素

    从类来看,抽象基类和具体子类

    抽象基类中,

    1)有基本方法,boilWater(),pourInCup(),对子类而言是相同的,具有共性的,直接在抽象基类中定义的方法

    2)有抽象方法,brew(), addCondiments(),只知道具体原则,而不知道具体的实现细节,需要延迟到子类中去进行实现的步骤

    3)可选的钩子,钩子函数在基类中提供的默认或者为空的实现,以供给子类决定是否挂载或者如何挂载,从而影响算法实现的方法

    4)Template方法(final),将基本方法,抽象方法,钩子方法汇按照业务逻辑需求汇总成模板方法。注意:声明为final,不能为子类所覆写。

    5)子类可以替换掉父类中的可变逻辑,但不能改变整体逻辑结构。

    具体子类中,

    1)实现基类中的抽象方法,提供个性化的具体实现。如冲泡咖啡,浸泡茶叶

    2)覆盖钩子方法,个性化影响算法的局部的行为。如泡茶时不加入柠檬,覆写钩子方法;冲咖啡的时候要加入糖和牛奶,没有覆写钩子方法。

    模板方法的实现要素:

    准备一个抽象类,将部分逻辑以具体方法的形式实现,然后声明一些抽象方法交由子类实现剩余逻辑,用钩子方法给予子类更大的灵活性。最后将方法汇总构成一个不可改变的模板方法。

    模板方法的适用场景:

    1)算法或操作遵循相似的逻辑

    2)重构时(把相同的代码抽取到父类中,以便于代码复用,共性抽象模板方法,个性交给子类实现)

    3)重要,复杂的算法,核心算法设计为模板算法

    模板方法的优点:

    1)封装性好

    2)复用性好(如办理银行业务)

    3)屏蔽细节

    4)便于维护

    模板方法的缺点:

    继承,单继承语言,一个类只能extends一个父类,通过模板方法模式中引入新的继承,会有问题。

    行业案例:

    电信运行商系统,分析处理各种日志

    需求分析:种类繁多数量巨大的日志,先抽取共性,再获得规律性的东西

    规律:1,2,3,5都是一样的,只有4不同,所以可以应用模板方法模式

    1、获得文件 --抽象基类

    2、打开文件 --抽象基类

    3、读取日志结构 --抽象基类

    4、处理单行日志  --延迟到子类实现

    5、清理工作 --抽象基类

  • 相关阅读:
    三、linux系统管理
    二、基本命令
    一、基本环境
    mysql-day4
    mysql-day3
    mysql-day2
    mysql-day1
    3、线性表的链式存储结构
    2、线性表之顺序表
    1、时间复杂度
  • 原文地址:https://www.cnblogs.com/nikey/p/6496619.html
Copyright © 2011-2022 走看看