zoukankan      html  css  js  c++  java
  • 设计模式之模版方法模式(Template Method Pattern)

    一.什么是模版方法模式?

    首先,模版方法模式是用来封装算法骨架的,也就是算法流程

    既然被称为模版,那么它肯定允许扩展类套用这个模版,为了应对变化,那么它也一定允许扩展类做一些改变

    事实就是这样,模版方法模式封装了算法流程,但允许由子类负责实现某些步骤细节

    二.举个例子

    假设我们要开一家允许加盟的炸酱面店,我们拥有独家秘制的酱料配方,以及独特的制作工艺,美味只此一家,地球上大多数人每天都吃我们的炸酱面,所以我们有了大量的加盟者

    为了盈利,我们当然不能公开制作工艺与酱料配方,但由于加盟者经营地域的差异,制作细节又存在差异,比如北京人喜欢吃细面硬面,陕西人喜欢吃宽面软面,我们也不能为了保护配方而让北京的加盟者继续卖我们的宽面

    所以我们需要把制作工艺与保密配方保护起来,谁都不允许修改,同时把面条的制作以及烹煮方式公开出去,允许加盟者修改

    那么,要如何实现这样的需求?

    没错,模版方法就是为此而生的

    -------

    首先,我们需要建立模版类,把该保护的保护起来,把该公开的公开出去:

    package TemplateMethodPattern;
    
    /**
     * @author ayqy
     * 定义炸酱面模版类
     */
    public abstract class Noodles {
    	public void cook(){
    		//制作面条
    		makeNoodles();
    		//制作酱料
    		makeSauce();
    		//烹煮面条
    		boilNoodles();
    		//添加酱料
    		addSauce();
    	}
    	
    	/**
    	 * 把秘制酱料保护起来
    	 */
    	private void makeSauce(){
    		System.out.println("做好一份独家秘制酱料");
    	}
    	
    	/**
    	 * 把添加剂量保护起来
    	 */
    	private void addSauce(){
    		System.out.println("添加适量的秘制酱料");
    	}
    	
    	public abstract void makeNoodles();
    	public abstract void boilNoodles();
    }
    

    定义好了模版,90%的工作就已经完成了,下面开始实现具体类

    北京炸酱面:

    package TemplateMethodPattern;
    
    /**
     * @author ayqy
     * 实现北京炸酱面具体类
     */
    public class BJNoodles extends Noodles{
    
    	@Override
    	public void makeNoodles() {
    		System.out.println("做好一份有北京特色的手工面");
    	}
    
    	@Override
    	public void boilNoodles() {
    		System.out.println("按北京特色煮面法煮好面条");
    	}
    }
    

    陕西炸酱面:

    package TemplateMethodPattern;
    
    /**
     * @author ayqy
     * 实现陕西炸酱面具体类
     */
    public class SXNoodles extends Noodles{
    
    	@Override
    	public void makeNoodles() {
    		System.out.println("做好一份有陕西特色的手工面");
    	}
    
    	@Override
    	public void boilNoodles() {
    		System.out.println("按陕西特色煮面法煮好面条");
    	}
    }
    

    有了这些具体类,我们按照地域分配给加盟者就好了

    三.效果示例

    先实现一个测试类:

    package TemplateMethodPattern;
    
    /**
     * @author ayqy
     * 实现一个测试类
     */
    public class Test {
    	public static void main(String[] args){
    		//创建北京炸酱面对象
    		Noodles bjnoodles = new BJNoodles();
    		//创建陕西炸酱面对象
    		Noodles sxnoodles = new SXNoodles();
    		
    		System.out.println("北京炸酱面制作工艺:");
    		bjnoodles.cook();//做一份北京炸酱面
    		System.out.println("
    陕西炸酱面制作工艺:");
    		sxnoodles.cook();//做一份陕西炸酱面
    	}
    }
    

    运行结果:

    四.多一点思考

    上面的例子中,我们很轻松的实现了对算法骨架的封装,而且允许扩展类自定义某些步骤细节,似乎很轻松也很完美

    那好,让我们改改需求吧:

    最近提倡绿色健康生活,大家都喜欢吃点蔬菜,我们传统的炸酱面不得不与时俱进,做好之后还要添点蔬菜

    但问题是有的地方并不喜欢添加蔬菜,他们习惯了老字号炸酱面的风味,坚决不要蔬菜

    所以我们不能简单地改变制作工艺,添加一道工序来加蔬菜

    -------

    如果能在模版中定义一个可选的操作就再好不过了,让加盟者自己选择要不要加点儿蔬菜

    于是,我们需要对之前的模版做一点点改动:

    package TemplateMethodPattern;
    
    /**
     * @author ayqy
     * 定义炸酱面模版类
     */
    public abstract class Noodles {
    	private boolean wantVegetables = false;//要不要蔬菜
    	
    	public void setWantVegetables(boolean wantVegetables) {
    		this.wantVegetables = wantVegetables;
    	}
    
    	public void cook(){
    		//制作面条
    		makeNoodles();
    		//制作酱料
    		makeSauce();
    		//烹煮面条
    		boilNoodles();
    		
    		//要不要添点儿蔬菜
    		if(wantVegetables)
    			addVegetables();
    		
    		//添加酱料
    		addSauce();
    	}
    	
    	/**
    	 * 把秘制酱料保护起来
    	 */
    	private void makeSauce(){
    		System.out.println("做好一份独家秘制酱料");
    	}
    	
    	/**
    	 * 把添加剂量保护起来
    	 */
    	private void addSauce(){
    		System.out.println("添加适量的秘制酱料");
    	}
    	
    	/**
    	 * 添加时令蔬菜
    	 */
    	public void addVegetables(){
    		//空的实现
    	}
    	
    	public abstract void makeNoodles();
    	public abstract void boilNoodles();
    }
    

    注意,为了添加可选的操作,我们做了这么几件事情:

    1. 定义标志变量(选/不选)
    2. 提供标志变量的setter,供扩展类选择
    3. 修改算法骨架,加入新的可选步骤
    4. 为新的步骤提供一个空的实现(注意,这一步很重要,提供空的实现而不是定义一个抽象方法,避免了对现有扩展类的修改,这个在模版方法模式的术语中被称为Hook钩子

    五.模版方法模式与策略模式

    这两个模式都是用来封装算法的,让我们来对比一下:

      策略模式 模版方法模式
    概念 封装算法步骤,允许子类选择已有的策略(步骤细节) 封装算法骨架(流程),允许由子类负责实现某些细节
    实现方式 用组合来实现 用继承来实现
    目标 实现了算法步骤的选择 实现了算法的流程控制
    亮点 支持运行时动态改变步骤(策略) 支持运行时动态改变算法流程(用hook来实现)
    具体步骤 把易于变化的同类算法细节(步骤)找出来,再定义一个算法族(接口)把它们封装起来 把算法骨架抽象出来并封装在基类(模版类)中
    总结 封装步骤 封装流程

    举个例子:

    假设现在有一个算法,流程是A->B->C->D,C步骤的具体实现可能有c1,c2,c3三种不同方法

    策略模式:

    1. 定义一个行为接口C,定义execute方法
    2. 实现具体类c1,c2,c3扩展自接口C(c1,c2,c3三种行为供调用者选择)
    3. 在基类中添加一个属性,类型为接口C,并提供setter
    4. 扩展自基类的具体类将通过调用setter来动态改变行为

    模版方法模式:

    1. 定义基类(模版类),在基类中定义算法流程(把流程封装起来)
    2. 在基类中把C步骤定义为抽象方法
    3. 扩展自基类的具体类将提供自己的实现(c1,c2,c3或者其它)
  • 相关阅读:
    sql去重复
    验证 decimal 和 数字
    OleDbConnection读取Excel
    排班知识点
    sql基础
    SQL Server 获取月份的具体天数
    2016 Excel 展示 Sqlserver数据并制作图表
    SQL Server跨域查询
    SqlSugar中CASE WHEN的用法
    Microsoft.AspNetCore.Mvc.Versioning学习笔记
  • 原文地址:https://www.cnblogs.com/ayqy/p/3992724.html
Copyright © 2011-2022 走看看