Java设计模式之模板方法模式

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


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


模板方法模式的结构

模板方法模式是所有模式中最为常见的几个模式之一,是基于继承的代码复用的基本技术。模板方法模式的静态结构如下图所示:


抽象基类(Abstract Template):
  1、定义了一个或多个抽象操作,以便让子类实现。这些抽象操作叫做基本操作,它们是一个顶级逻辑的组成步骤。
  2、定义并实现了一个模板方法。这个模板方法一般是一个具体方法,它给出了一个顶级逻辑的骨架,而逻辑的组成步骤在相应的抽象操作中,推迟到子类实现。顶级逻辑也有可能调用一些具体方法。
具体子类(Concrete Template):
  1、实现父类所定义的一个或多个抽象方法,它们是一个顶级逻辑的组成步骤。
  2、每一个抽象模板角色都可以有任意多个具体模板角色与之对应,而每一个具体模板角色都可以给出这些抽象方法(也就是顶级逻辑的组成步骤)的不同实现,从而使得顶级逻辑的实现各不相同。


接下来源码:

抽象基类(AbstractTemplate),abstractMethod()、hookMethod()等基本方法是顶级逻辑的组成步骤,这个顶级逻辑由templateMethod()方法代表,并且顶级逻辑是不可改变的。

/**
 * 抽象模板角色类,abstractMethod()、hookMethod()等基本方法是顶级逻辑的组成步骤,
 * 这个顶级逻辑由templateMethod()方法代表。
 * 
 * @author JUN
 * 
 */
public abstract class AbstractTemplate {

	// 模板方法 (不可改动的)
	public final void templateMethod() {
		// 调用基本方法
		abstractMethod();
		hookMethod();
		concreteMethod();
	}

	// 基本方法的声明(由子类实现)必须由子类置换
	protected abstract void abstractMethod();

	// 基本方法(空方法)可以由子类置换
	protected void hookMethod() {
	}

	// 基本方法(已经实现)子类不可以动
	private final void concreteMethod() {
		// 业务相关的代码
	}
}

具体子类(ConcreteTemplate),实现了父类所声明的基本方法,abstractMethod()方法所代表的就是强制子类实现的剩余逻辑,而hookMethod()方法是可选择实现的逻辑,不是必须实现的。

/**
 * 具体模板角色类,实现了父类所声明的基本方法,
 * abstractMethod()方法所代表的就是强制子类实现的剩余逻辑,
 * 而hookMethod()方法是可选择实现的逻辑,不是必须实现的。 
 * @author JUN
 *
 */
public class ConcreteTemplate extends AbstractTemplate {

    // 基本方法的实现  
    @Override  
    public void abstractMethod() {  
        // 业务相关的代码  
    }  
  
    // 重写父类的方法  
    @Override  
    public void hookMethod() {  
        // 业务相关的代码  
    }

}

模板方法模式的关键是:子类可以置换掉父类的可变部分,但是子类却不可以改变模板方法所代表的顶级逻辑,以及基本实现。

  每当定义一个新的子类时,不要按照控制流程的思路去想,而应当按照“责任”的思路去想。换言之,应当考虑哪些操作是必须置换掉的,哪些操作是可以置换掉的,以及哪些操作是不可以置换掉的。使用模板模式可以使这些责任变得清晰。


模板方法模式的实现要素:
抽象基类:
1、基本方法(final)
2、抽象方法
3、可选的钩子方法
4、模板方法(final)

具体子类:
1、实现基类中的抽象方法
2、根据需求选择去覆盖钩子方法


接下来我们用形象的例子实现一下:



源码:

抽象基类(RefreshBeverage):

/**
 * 抽象基类,为所有子类提供一个算法框架(提神饮料)
 * 
 * @author JUN
 * 
 */
public abstract class RefreshBeverage {
	
	/**
	 * 制备饮料的模板方法
	 * 封装了所有子类共同遵循的算法框架
	 */
	public final void prepareBeverageTemplate() {
//		1、把水煮沸(boilWater)
		boilWater();
//		2、泡饮料(brew)
		brew();
//		3、把饮料倒进杯子(pourInCup)
		pourInCup();
//		4、加调味料(addCondiments)
		if(isWaters()){
			addCondiments();
		}
		
	}

	//询问用户是否加入调料,Hook方法(钩子函数)
	protected boolean isWaters() {
		return true;
	}

	//基本方法,把水煮沸
	private final void boilWater() {
		System.out.println("把水煮沸");
		
	}


	//基本方法,把饮料倒进杯子
	private final void pourInCup() {
		System.out.println("把饮料倒进杯子");
		
	}
	
	//抽象的基本方法,泡制饮料
	protected abstract void brew();
	
	//抽象的基本方法,加入调味料
	protected abstract void addCondiments();
}


具体子类(Coffee):

/**
 * 具体的子类,提供了咖啡制备的具体实现
 * @author JUN
 *
 */
public class Coffee extends RefreshBeverage {

	@Override
	protected void brew() {
		System.out.println("用沸水冲泡咖啡");

	}

	@Override
	protected void addCondiments() {
		System.out.println("加糖和牛奶");

	}

}

具体子类(Tea):

/**
 * 具体的子类,提供了制备茶的具体实现
 * @author JUN
 *
 */
public class Tea extends RefreshBeverage {

	@Override
	protected void brew() {
		System.out.println("用沸水浸泡茶叶");

	}

	@Override
	protected void addCondiments() {
		System.out.println("加柠檬");

	}
	
	//不加柠檬,我要一杯纯正的茶水
	@Override
	protected boolean isWaters(){
		return false;
		
	}

}


接下来我们新建一个客户端类来测试一下:

public class Client {
	public static void main(String[] args) {
		//冲咖啡
		System.out.println("冲咖啡啦");
		RefreshBeverage c = new Coffee();
		c.prepareBeverageTemplate();
		
		System.out.println("---------------");
		//冲茶
		System.out.println("冲茶啦");
		RefreshBeverage t = new Tea();
		t.prepareBeverageTemplate();
	}
}


运行结果:


总结:
优点:

1、封装性好
2、复用性好
3、屏蔽细节
4、便于维护

缺点:
使用模板方法模式,使得我们的类已经处于继承状态不能在继承其他类了(Java单继承)

模板方法的使用场景:
1、算法或操作遵循相似的逻辑
2、重构时(把相同的代码抽取到父类中)
3、重要、复杂的算法,核心算法设计为模板算法

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值