设计模式之模板方法模式

模板方法模式也是也是比较容易理解的,就比如说做饭,同样的步骤不同的人做味道是不一样的。或者是造汽车,同样的步骤,造车厂商不一样,造出来的汽车质量不同。这就是模板方法模式。这篇文章将通过案例详细的讲解一下模板方法模式。

1、概念

它定义一个操作中的算法的框架,而将一些步骤延迟到了子类中。使得子类可以不改变一个算法的结构即可重定义该算法的某些步骤。

2、例子

我们拿做饭的例子来说明。比如就做个西红柿鸡蛋吧。我们可以简单地定义一下步骤:

第一步:放油

第二步:放西红柿

第三步:放鸡蛋

当然真正做起来可能要比这复杂的多,就不必纠结于此了。上面我们提到过,同样的步骤其实不同的人做出来的饭是不一样的。我们就拿自己和五星级大厨来比较吧。人总是要有点自信心,虽然会被打脸。

不过在代码实现之前,我们可以先看一下模板方法模式的类图:

在这里插入图片描述

我们就拿自己和大厨比较,我们可以看到在这里其实有两个角色:

(1)模板方法:他定义了一系列方法,提供了一个骨架。

(2)具体类:实现上面模板方法类提供的骨架。不同的具体类实现这个模板方法的骨架方式是不一样的。

下面代码实现一下。

3、代码实现

第一步:定义模板方法类(cook的骨架)

public abstract class Cook {
	//放油
	public abstract void oil();
	//放鸡蛋
	public abstract void egg();
	//放西红柿
	public abstract void tomato();
	
	//把做饭的方法封装起来
	final public void cook(){  
		this.oil();
		this.egg();
		this.tomato();
	}
}

第二步:定义具体类(我和大厨)

首先是我做饭

public class MeCook extends Cook{
	@Override
	public void oil() {
		System.out.println("自己:放十斤油");
	}
	@Override
	public void egg() {
		System.out.println("自己:放一个鸡蛋");
	}
	@Override
	public void tomato() {
		System.out.println("自己:放10个鸡蛋");
	}
}

然后是大厨做饭

public class ChefCook extends Cook {
	@Override
	public void oil() {
		System.out.println("大厨:放适量油");
	}
	@Override
	public void egg() {
		System.out.println("大厨:放适量鸡蛋");
	}
	@Override
	public void tomato() {
		System.out.println("大厨:放适量西虹市");
	}
}

第三步:模拟炒菜的过程

public class Test {
	public static void main(String[] args) {
		Cook myCook=new MeCook();
		myCook.cook();
		Cook chefCook=new ChefCook();
		chefCook.cook();
	}
}
//output:
//自己:放十斤油
//自己:放一个鸡蛋
//自己:放10个鸡蛋
//大厨:放适量油
//大厨:放适量鸡蛋
//大厨:放适量西虹市

看结果我们就能知道,炒西红柿鸡蛋的过程是一样的,但是实现起来却不一样,就像买衣服,模特身上穿起来很好看,但是自己买的时候穿起来那真是惨不忍睹,这就是模板方法模式,我们对其进行一个总结。

4、优缺点

先说一下他的优点吧:

(1)把不可改变的封装起来,把能够改变的扩展开来

(2)他把很多类的共同操作给封装了起来,利于维护

(3)其实我们发现,我们在定义行为时候都是由父类去定义,然后子类去实现即可。

再聊一下他的缺点:

缺点很简单,我们发现虽然我们把一些类的共同操作封装了起来,但是当这些类比较多时,效果就不好了,因为有一个拓展子类都需要继承它,子类多了就不好了。

5、钩子函数

钩子就是给子类一个授权,让子类来决定模板方法的逻辑执行。就比如在炒西红柿鸡蛋的时候,由子类去决定是否要加调料。我们去实现一下:

第一步:定义模板类:

public abstract class Cook {
	//放油
	public abstract void oil();
	//放鸡蛋
	public abstract void egg();
	//放西红柿
	public abstract void tomato();
	
	//钩子方法:让子类决定是否加油
	public boolean isAddOil(){
	    return true;
	}
	//把做饭的方法封装起来
	final public void cook(){  
		this.egg();
		this.tomato();
		//如果子类决定添加:则执行添加油的方法
		if(this.isAddOil()) {
			this.oil();
		}
	}
}

第二步:定义具体类

首先是自己:

public class MeCook extends Cook{
	//默认添加油
	private boolean addOilFlag=true;
	//用户设置是否添加油
	public void setIsAddOil(boolean isAddOil) {
		addOilFlag=isAddOil;
	}
	//子类传递过来的命令决定
	@Override
	public boolean isAddOil() {
		return this.addOilFlag;
	}
	@Override
	public void oil() {
		System.out.println("自己:放十斤油");
	}
	@Override
	public void egg() {
		System.out.println("自己:放一个鸡蛋");
	}
	@Override
	public void tomato() {
		System.out.println("自己:放10个鸡蛋");
	}
}

然后是大厨

public class ChefCook extends Cook {
	//默认添加油
	private boolean addOilFlag=true;
	//用户设置是否添加油
	public void setSeasoning(boolean isAddOil) {
		isAddOil=isAddOil;
	}
	//子类传递过来的命令决定
	@Override
	public boolean isAddOil() {
		return this.addOilFlag;
	}
	@Override
	public void oil() {
		System.out.println("大厨:放适量油");
	}
	@Override
	public void egg() {
		System.out.println("大厨:放适量鸡蛋");
	}
	@Override
	public void tomato() {
		System.out.println("大厨:放适量西虹市");
	}
}

第三步:测试一下:

public class Test {
	public static void main(String[] args) {
		//我不放油
		MeCook myCook=new MeCook();
		myCook.setIsAddOil(false);
		myCook.cook();
		//大厨做饭放油是默认的,不用指定
		Cook chefCook=new ChefCook();
		chefCook.cook();
	}
}
//output
//自己:放一个鸡蛋
//自己:放10个鸡蛋
//大厨:放适量鸡蛋
//大厨:放适量西虹市
//大厨:放适量油

钩子函数就是这样使用的。子类就可以设置其逻辑是否执行。自己动手写一遍是理解最好的方式。

欢迎关注微信公众号:java的架构师技术栈。回复指定关键字可获取编程技术各种视频资源等,包含java基础、进阶、框架、架构师系列。python、Android、微信小程序、神经网络、机器学习等等各种资源

ook();
}
}
//output
//自己:放一个鸡蛋
//自己:放10个鸡蛋
//大厨:放适量鸡蛋
//大厨:放适量西虹市
//大厨:放适量油
``

钩子函数就是这样使用的。子类就可以设置其逻辑是否执行。自己动手写一遍是理解最好的方式。

欢迎关注微信公众号:java的架构师技术栈。回复指定关键字可获取编程技术各种视频资源等,包含java基础、进阶、框架、架构师系列。python、Android、微信小程序、神经网络、机器学习等等各种资源
在这里插入图片描述

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值