二、模版模式与回调Template Method(行为型模式)

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

我们使用冲泡咖啡和冲泡茶的例子

加工流程:

咖啡冲泡法:1.把水煮沸、2.用沸水冲泡咖啡、3.把咖啡倒进杯子、4.加糖和牛奶

茶冲泡法:   1.把水煮沸、2.用沸水冲泡茶叶、3.把  茶 倒进杯子、4.加蜂蜜

 

实践步骤:

1>创建一个模板(抽象)类:

Beverage(饮料)

public abstract class Beverage {
	/**
	 * 冲泡咖啡或茶...流程
	 */
	public final void create(){
		boilWater();//把水煮沸
		brew();//用沸水冲泡...
		pourInCup();//把...倒进杯子
		addCoundiments();//加...
	}


	public abstract void addCoundiments();

	public abstract void brew();
	
	public void boilWater() {
		System.out.println("煮开水");
	}
	
	public void pourInCup() {
		System.out.println("倒进杯子");
	}
}

2>创建一个咖啡类(Coffee)和茶(Tea)类,都继承Beverage抽象类

1.咖啡(Coffee)

public class Coffee extends Beverage{

	@Override
	public void addCoundiments() {
		System.out.println("添加糖和牛奶");		}

	@Override
	public void brew() {
		System.out.println("用水冲咖啡");
	}
}

2.茶(Tea)

public class Tea extends Beverage{

	@Override
	public void addCoundiments() {
		System.out.println("添加蜂蜜");
	}

	@Override
	public void brew() {
		System.out.println("用水冲茶");
	}

}

 public class Test {
	public static void main(String[] args) {
		Coffee coffee = new Coffee();
		coffee.create();//冲泡咖啡
		
		//Tea tea = new Tea();//冲泡茶
		//tea.create();
	}
}

运行结果:

-----------------------------------

煮开水

用水冲咖啡

倒进杯子

添加糖和牛奶

-----------------------------------


在模版模式中使用挂钩(hook)

存在一个空实现的方法,我们称这种方法为”hook”。子类可以视情况来决定是否要覆盖它。

1>我们对模板类(Beverage)进行修改

public abstract class Beverage {
	/**
	 * 冲泡咖啡或茶...流程
	 */
	public final void create(){
		boilWater();//把水煮沸
		brew();//用沸水冲泡...
		pourInCup();//把...倒进杯子
		addCoundiments();//加...
		
		hook();//挂钩
	}
	//空实现方法
	public void hook(){}

	public abstract void addCoundiments();

	public abstract void brew();
	
	public void boilWater() {
		System.out.println("煮开水");
	}
	
	public void pourInCup() {
		System.out.println("倒进杯子");
	}
}

 2>假如我们搞活动,喝一杯咖啡送一杯,修改咖啡(Coffee)类

public class Coffee extends Beverage{

	@Override
	public void addCoundiments() {
		System.out.println("添加糖和牛奶");	}

	@Override
	public void brew() {
		System.out.println("用水冲咖啡");
	}
	
	/**
	 * 挂钩
	 */
	@Override
	public void hook() {
		System.out.println("再来一杯");
	}

}

 3>使用上面的测试类

运行结果:

--------------------------------

 

煮开水

用水冲咖啡

倒进杯子

添加糖和牛奶

再来一杯

--------------------------------

结果中有“再来一杯”...


我们也可以这样使用挂钩,让其决定里面的代码是否执行

1>我们对模板类(Beverage)进行修改

public abstract class Beverage {
	/**
	 * 冲泡咖啡或茶...流程
	 */
	public final void create(){
		boilWater();//把水煮沸
		brew();//用沸水冲泡...
		pourInCup();//把...倒进杯子
		
		//挂钩决定是否添加配料
		if(hook()){
			addCoundiments();//加...
		}
		//hook();
	}

	/**
	 * 默认添加配料
	 * @return
	 */
	public boolean hook() {
		return true;
	}

	//public void hook(){}
	
	public abstract void addCoundiments();

	public abstract void brew();
	
	public void boilWater() {
		System.out.println("煮开水");
	}
	
	public void pourInCup() {
		System.out.println("倒进杯子");
	}
}

 2>我们对Coffee类进行修改,让其不添加配料

public class Coffee extends Beverage{

	@Override
	public void addCoundiments() {
		System.out.println("添加糖和牛奶");		}

	@Override
	public void brew() {
		System.out.println("用水冲咖啡");
	}
	
	/**
	 * 有的客人不喜欢加配料
	 */
	@Override
	public boolean hook() {
		return false;
	}
	
	/*@Override
	public void hook() {
		System.out.println("再来一杯");
	}
*/
}

3>还是使用上面的测试类

运行结果:

------------------------------------------------------

 

煮开水

用水冲咖啡

倒进杯子

------------------------------------------------------

运行结果中没有添加配料


关于模板模式

1>模板模式定义了算法的步骤,把这些步骤的实现延迟到子类

2>模板模式为我们提供了一个代码复用的技巧

3>模板抽象类中可以定义具体方法、抽象方法和钩子方法

4>为了防止子类改变模板中的算法,可以将模板方法声明为final

5>钩子是一种方法,它在抽象类中不做事,或只做默认的事,子类可以选择要不要实现它



一.Java回调与模板方法模式 

模板方法模式很常用,其目的是在一个方法中定义一个算法骨架,而将一些步骤延迟到子类中。模板方法使得子类可以在不改变算法结构的情况下,重新定义算法的某些步骤。在标准的模板方法模式实现中,主要是使用继承的方式,来让父类在运行期间可以调用到子类的方法。 其实在Java开发中,还有另外一个方法可以实现同样的功能,那就是Java回调技术,通过回调在接口中定义的方法,调用到具体的实现类中的 方法,其本质是利用Java的动态绑定技术,在这种实现中,可以不把实现类写成单独的类,而使用内部类或匿名内部类来实现回调方法。 

 

二.回调方法应用举例

1.应用场景:

在实际业务中处理中,有这样一种场景,我们会在业务的开始设置线程上下文变量,在业务结束时对线程上下文变量进行清空,很类似于JDBC的操作后对数据库资源的释放,我们可以借助回调方法实现其执行步骤。


2.代码实现

(1)定义业务模板,process()方法就是算法步骤。

public class BusinessTemplate<T> {	
	private ProcessCallback<T> processCallback;
		
	public BusinessTemplate(ProcessCallback<T> callback) {
		this.processCallback = callback;
	}
		
			/**
			 * 模板方法
			 */
	public T process() {
		try {
		System.out.println(ContextHolder.getContext());
		return processCallback.process();
		} finally {
			ContextHolder.clear();
		}
	}
}

(2)回调处理类

public interface ProcessCallback<T> {
			/**
			 * 回调处理。
			 * @return
			 */
<span style="white-space:pre">	</span>T process();
}

(3)ContextHolder类  

public class ContextHolder {
		
			private static final ThreadLocal<Context> threadLocal = new ThreadLocal<Context>();
		
			public static void setContext(Context context) {
				threadLocal.set(context);
			}
		
			public static Context getContext() {
				return threadLocal.get();
			}
		
			public static void clear() {
				threadLocal.remove();
			}
}
	

(4)测试类

public class TestCallBack {
			public static void main(String[] args) {
				Context context = new Context();
				context.setBusinessName("name");
				context.setBusinessType("type");
				ContextHolder.setContext(context);
		
				BusinessTemplate<BusinessResult> template = new BusinessTemplate<BusinessResult>(
						new ProcessCallback<BusinessResult>() {
							public BusinessResult process() {
								BusinessResult businessResult = new BusinessResult();
								return businessResult;
							}
						});
		
				template.process();
		
				System.out.print(ContextHolder.getContext());
			}
}
	

三.两种实现方式的比较 

1.模板方法模式借助于继承,对抽象方法在子类中进行扩展或实现,是在编译期间静态决定的,是类级关系。使用Java回调方法,利用动态绑定技术在运行期间动态决定的,是对象级的关系。

2.使用回调机制会更灵活,因为Java是单继承的,如果使用继承的方式,对于子类而言,今后就不能继承其它对象了。而使用回调,是基于接口的,方便扩展。 另外,如果有多个子类都要使用模板方法,则所有的子类都要实现模板方法,无形中增多了子类的个数。

3.使用模板方法模式使用继承方式会更简单点,因为父类提供了实现的方法,子类如果不想扩展,那就不用管。如果使用回调机制,回调的接口需要把所有可能被扩展的 方法都定义进去,这就导致实现的时候,不管你要不要扩展,你都要实现这个方法,哪怕你什么都不做,只是转调模板中已有的实现,都要写出来。








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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值