设计模式入门学习之模板方法模式(封装算法)
关键字: 设计模式有些人没有咖啡就活不下去,有些人则离不开茶。两者共同的成分是什么?当然是咖啡因了!但还不只这样,茶和咖啡的冲泡方式非常相似:
一、咖啡冲泡法
1.把水煮沸
2.用沸水冲泡咖啡
3.把咖啡倒进杯子
3.加糖和牛奶
二、茶冲泡法
1.把水煮沸
2.用沸水浸跑茶叶
3.把茶倒进杯子
4.加柠檬
让我们写一些代码来快速搞定咖啡和茶的类
public class Coffee {
//这是咖啡冲泡法,直接取自上面的流程,每个步骤都被实现在分离的方法中
void prepareRecipe() {
boilWater();
brewCoffeeGrinds();
pourInCup();
addSugarAndMilk();
}
public void boilWater() {
System.out.println("Boiling water");
}
public void brewCoffeeGrinds() {
System.out.println("Dripping Coffee through filter");
}
public void pourInCup() {
System.out.println("Pouring into cup");
}
public void addSugarAndMilk() {
System.out.println("Adding Sugar and Milk");
}
}
public class Tea {
//茶冲泡法跟咖啡类似,但2、4这两个步骤不一样,是泡茶专有的
void prepareRecipe() {
boilWater();
steepTeaBag();
pourInCup();
addLemon();
}
public void boilWater() {
System.out.println("Boiling water");
}
public void steepTeaBag() {
System.out.println("Steeping the tea");
}
public void addLemon() {
System.out.println("Adding Lemon");
}
public void pourInCup() {
System.out.println("Pouring into cup");
}
}注意,泡茶里面的第1、3和冲咖啡方法是一样的,也就是说这里有重复的代码,所以,咖啡和茶还有什么其他的共同点呢?
注意两份冲泡法都采用了相同的算法:
1.把水煮沸
2.用热水泡咖啡或茶
3.把饮料倒进杯子
4.在饮料内加入适当的调料
那么我们有办法将prepareRecipe()也抽象化吗?我们来看看。
第一个问题:咖啡使用brewCoffeeGrinds()和addSugarAndMilk()方法,而茶使用steepTeaBag()和addLemon()方法。浸泡(steep)和冲泡(brew)差异其实不大,所以我们给它新的方法名称brew(),类似加糖和加柠檬很相似,都是在饮料中加入调料,我们也给个新名称addCondiments()好了,这样一来,prepareRecipe()方法就是这样:
void prepareRecipe() {
boilWater();
brew();
pourInCup();
addCondiments();
}第二步,我们有了新的prepareRecipe方法,但是需要让她能够符合代码,我们先从CaffeineBeverage(咖啡因饮料)超类开始:
public abstract class CaffeineBeverage {//抽象类
//被声明为final,我们不希望子类覆盖这个方法
final void prepareRecipe() {
boilWater();
brew();
pourInCup();
addCondiments();
}
//因咖啡和茶处理这些方法的做法不同,所以这2个方法必须被声明为抽象的,剩余的给子类去操心
abstract void brew();
abstract void addCondiments();
//这2个方法不变,不需要子类去处理
void boilWater() {
System.out.println("Boiling water");
}
void pourInCup() {
System.out.println("Pouring into cup");
}
}最后,我们需要处理咖啡和茶类,这2个类现在都是依赖超类CaffeineBeverage来处理冲泡法,所以只需要自行处理冲泡和添加调料部分:
public class Tea extends CaffeineBeverage {
public void brew() {
System.out.println("Steeping the tea");
}
public void addCondiments() {
System.out.println("Adding Lemon");
}
}
public class Coffee extends CaffeineBeverage {
public void brew() {
System.out.println("Dripping Coffee through filter");
}
public void addCondiments() {
System.out.println("Adding Sugar and Milk");
}
}回过头去看看我们刚才做了些什么。基本上,我们刚刚实现的就是模板方法模式,模板方法定义了一个算法的步骤,并允许子类为一个或多个步骤提供实现。
模板方法模式在一个方法中定义了一个算法的骨架,而将一些步骤延迟到子类中。模板方法使得子类可以在不改变算法结构的情况下,重新定义算法中的某些步骤。
钩子是一种被声明在抽象类中的方法,但只有空的或者默认的实现。钩子的存在,可以让子类有能力对算法的不同点进行挂钩,要不要挂钩,由子类自行决定。我们先来看看钩子的用途:
public abstract class CaffeineBeverageWithHook {
void prepareRecipe() {
boilWater();
brew();
pourInCup();
//我们加了一个小小的条件语句,而该条件是否成立,是由一个具体方法//customerWantsCondiments决定的。如果顾客“想要”调料时我们才调用addCondiments
if (customerWantsCondiments()) {
addCondiments();
}
}
abstract void brew();
abstract void addCondiments();
void boilWater() {System.out.println("Boiling water");}
void pourInCup() {System.out.println("Pouring into cup");}
//这里定义了一个方法,通常是空的缺省实现。
//这个就是一个钩子,子类可以覆盖这个方法,但不见得一定要这么做
boolean customerWantsCondiments() {
return true;
}
}
//我们如何得知顾客是否想要调料呢?开口问呀。。。
public class CoffeeWithHook extends CaffeineBeverageWithHook {
public void brew() {
System.out.println("Dripping Coffee through filter");
}
public void addCondiments() {
System.out.println("Adding Sugar and Milk");
}
//我们覆盖了这个钩子,提供自己的功能
public boolean customerWantsCondiments() {
//让用户输入他们对调料的决定,返回true或false
String answer = getUserInput();
if (answer.toLowerCase().startsWith("y")) {
return true;
} else {
return false;
}
}
//这个方法询问用户是否想要奶和糖,通过命令行获得用户输入
private String getUserInput() {
String answer = null;
System.out.print("Would you like milk and sugar with your coffee (y/n)? ");
BufferedReader in = new BufferedReader(new InputStreamReader(System.in));
try {
answer = in.readLine();
} catch (IOException ioe) {
System.err.println("IO error trying to read your answer");
}
if (answer == null) {
return "no";
}
return answer;
}
}
//好了,我们来测试一下
public class BeverageTestDrive {
public static void main(String[] args) {
//创建一杯咖啡
CoffeeWithHook coffeeHook = new CoffeeWithHook();
//然后调用prepareRecipe
System.out.println("\nMaking coffee...");
coffeeHook.prepareRecipe();
}
}我们有一个新的设计原则,称为好莱坞原则:
别调用我们,我们会调用你。
我们允许低层组件将自己挂钩到系统上,但是高层组件会决定什么时候和怎么使用这些低层组件。换句话说。高层组件对待低层组件的方式是“别调用我们,我们会调用你”
发表评论
- 浏览: 20854 次
- 性别:

- 来自: 北京

- 详细资料
搜索本博客
我的相册
共 30 张
最近加入圈子
最新评论
-
使用iBatis的自动化代码生 ...
有没有根据类生产数据库和xml的?
-- by aninfeel -
使用iBatis的自动化代码生 ...
IBatis去除注释版 http://hugh-lin.javaeye.com/ ...
-- by hugh-lin -
使用iBatis的自动化代码生 ...
我的不能用啊,郁闷掉了,真的是很黄很暴力。
-- by horror -
搭建Cloud Computing测试 ...
to fredzhang : 呵呵,我只是说用hadoop/hbase来搭 ...
-- by blank -
搭建Cloud Computing测试 ...
1. gfs的论文你仔细阅读过了吗?它的思想决定了基于它应当如何来实现,如何才能 ...
-- by fredzhang






评论排行榜