设计模式入门学习之模板方法模式(封装算法)
关键字: 设计模式
有些人没有咖啡就活不下去,有些人则离不开茶。两者共同的成分是什么?当然是咖啡因了!但还不只这样,茶和咖啡的冲泡方式非常相似:
一、咖啡冲泡法
1.把水煮沸
2.用沸水冲泡咖啡
3.把咖啡倒进杯子
3.加糖和牛奶
二、茶冲泡法
1.把水煮沸
2.用沸水浸跑茶叶
3.把茶倒进杯子
4.加柠檬
让我们写一些代码来快速搞定咖啡和茶的类
Java代码
1. public class Coffee {
2. //这是咖啡冲泡法,直接取自上面的流程,每个步骤都被实现在分离的方法中
3. void prepareRecipe() {
4. boilWater();
5. brewCoffeeGrinds();
6. pourInCup();
7. addSugarAndMilk();
8. }
9. public void boilWater() {
10. System.out.println("Boiling water");
11. }
12. public void brewCoffeeGrinds() {
13. System.out.println("Dripping Coffee through filter");
14. }
15. public void pourInCup() {
16. System.out.println("Pouring into cup");
17. }
18. public void addSugarAndMilk() {
19. System.out.println("Adding Sugar and Milk");
20. }
21. }
22. public class Tea {
23. //茶冲泡法跟咖啡类似,但2、4这两个步骤不一样,是泡茶专有的
24. void prepareRecipe() {
25. boilWater();
26. steepTeaBag();
27. pourInCup();
28. addLemon();
29. }
30. public void boilWater() {
31. System.out.println("Boiling water");
32. }
33. public void steepTeaBag() {
34. System.out.println("Steeping the tea");
35. }
36. public void addLemon() {
37. System.out.println("Adding Lemon");
38. }
39. public void pourInCup() {
40. System.out.println("Pouring into cup");
41. }
42. }
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()方法就是这样:
Java代码
1. void prepareRecipe() {
2. boilWater();
3. brew();
4. pourInCup();
5. addCondiments();
6. }
void prepareRecipe() {
boilWater();
brew();
pourInCup();
addCondiments();
}
第二步,我们有了新的prepareRecipe方法,但是需要让她能够符合代码,我们先从CaffeineBeverage(咖啡因饮料)超类开始:
Java代码
1. public abstract class CaffeineBeverage {//抽象类
2. //被声明为final,我们不希望子类覆盖这个方法
3. final void prepareRecipe() {
4. boilWater();
5. brew();
6. pourInCup();
7. addCondiments();
8. }
9. //因咖啡和茶处理这些方法的做法不同,所以这2个方法必须被声明为抽象的,剩余的给子类去操心
10. abstract void brew();
11. abstract void addCondiments();
12. //这2个方法不变,不需要子类去处理
13. void boilWater() {
14. System.out.println("Boiling water");
15. }
16. void pourInCup() {
17. System.out.println("Pouring into cup");
18. }
19. }
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来处理冲泡法,所以只需要自行处理冲泡和添加调料部分:
Java代码
1. public class Tea extends CaffeineBeverage {
2. public void brew() {
3. System.out.println("Steeping the tea");
4. }
5. public void addCondiments() {
6. System.out.println("Adding Lemon");
7. }
8. }
9. public class Coffee extends CaffeineBeverage {
10. public void brew() {
11. System.out.println("Dripping Coffee through filter");
12. }
13. public void addCondiments() {
14. System.out.println("Adding Sugar and Milk");
15. }
16. }
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");
}
}
回过头去看看我们刚才做了些什么。基本上,我们刚刚实现的就是模板方法模式,模板方法定义了一个算法的步骤,并允许子类为一个或多个步骤提供实现。
模板方法模式在一个方法中定义了一个算法的骨架,而将一些步骤延迟到子类中。模板方法使得子类可以在不改变算法结构的情况下,重新定义算法中的某些步骤。
钩子是一种被声明在抽象类中的方法,但只有空的或者默认的实现。钩子的存在,可以让子类有能力对算法的不同点进行挂钩,要不要挂钩,由子类自行决定。我们先来看看钩子的用途:
Java代码
1. public abstract class CaffeineBeverageWithHook {
2. void prepareRecipe() {
3. boilWater();
4. brew();
5. pourInCup();
6. //我们加了一个小小的条件语句,而该条件是否成立,是由一个具体方法//customerWantsCondiments决定的。如果顾客“想要”调料时我们才调用addCondiments
7. if (customerWantsCondiments()) {
8. addCondiments();
9. }
10. }
11. abstract void brew();
12. abstract void addCondiments();
13. void boilWater() {System.out.println("Boiling water");}
14. void pourInCup() {System.out.println("Pouring into cup");}
15. //这里定义了一个方法,通常是空的缺省实现。
16. //这个就是一个钩子,子类可以覆盖这个方法,但不见得一定要这么做
17. boolean customerWantsCondiments() {
18. return true;
19. }
20. }
21. //我们如何得知顾客是否想要调料呢?开口问呀。。。
22. public class CoffeeWithHook extends CaffeineBeverageWithHook {
23. public void brew() {
24. System.out.println("Dripping Coffee through filter");
25. }
26. public void addCondiments() {
27. System.out.println("Adding Sugar and Milk");
28. }
29. //我们覆盖了这个钩子,提供自己的功能
30. public boolean customerWantsCondiments() {
31. //让用户输入他们对调料的决定,返回true或false
32. String answer = getUserInput();
33. if (answer.toLowerCase().startsWith("y")) {
34. return true;
35. } else {
36. return false;
37. }
38. }
39. //这个方法询问用户是否想要奶和糖,通过命令行获得用户输入
40. private String getUserInput() {
41. String answer = null;
42. System.out.print("Would you like milk and sugar with your coffee (y/n)? ");
43. BufferedReader in = new BufferedReader(new InputStreamReader(System.in));
44. try {
45. answer = in.readLine();
46. } catch (IOException ioe) {
47. System.err.println("IO error trying to read your answer");
48. }
49. if (answer == null) {
50. return "no";
51. }
52. return answer;
53. }
54. }
55. //好了,我们来测试一下
56. public class BeverageTestDrive {
57. public static void main(String[] args) {
58. //创建一杯咖啡
59. CoffeeWithHook coffeeHook = new CoffeeWithHook();
60. //然后调用prepareRecipe
61. System.out.println("\nMaking coffee...");
62. coffeeHook.prepareRecipe();
63. }
64. }
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();
}
}
我们有一个新的设计原则,称为好莱坞原则:
别调用我们,我们会调用你。
我们允许低层组件将自己挂钩到系统上,但是高层组件会决定什么时候和怎么使用这些低层组件。换句话说。高层组件对待低层组件的方式是“别调用我们,我们会调用你”
关键字: 设计模式
有些人没有咖啡就活不下去,有些人则离不开茶。两者共同的成分是什么?当然是咖啡因了!但还不只这样,茶和咖啡的冲泡方式非常相似:
一、咖啡冲泡法
1.把水煮沸
2.用沸水冲泡咖啡
3.把咖啡倒进杯子
3.加糖和牛奶
二、茶冲泡法
1.把水煮沸
2.用沸水浸跑茶叶
3.把茶倒进杯子
4.加柠檬
让我们写一些代码来快速搞定咖啡和茶的类
Java代码
1. public class Coffee {
2. //这是咖啡冲泡法,直接取自上面的流程,每个步骤都被实现在分离的方法中
3. void prepareRecipe() {
4. boilWater();
5. brewCoffeeGrinds();
6. pourInCup();
7. addSugarAndMilk();
8. }
9. public void boilWater() {
10. System.out.println("Boiling water");
11. }
12. public void brewCoffeeGrinds() {
13. System.out.println("Dripping Coffee through filter");
14. }
15. public void pourInCup() {
16. System.out.println("Pouring into cup");
17. }
18. public void addSugarAndMilk() {
19. System.out.println("Adding Sugar and Milk");
20. }
21. }
22. public class Tea {
23. //茶冲泡法跟咖啡类似,但2、4这两个步骤不一样,是泡茶专有的
24. void prepareRecipe() {
25. boilWater();
26. steepTeaBag();
27. pourInCup();
28. addLemon();
29. }
30. public void boilWater() {
31. System.out.println("Boiling water");
32. }
33. public void steepTeaBag() {
34. System.out.println("Steeping the tea");
35. }
36. public void addLemon() {
37. System.out.println("Adding Lemon");
38. }
39. public void pourInCup() {
40. System.out.println("Pouring into cup");
41. }
42. }
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()方法就是这样:
Java代码
1. void prepareRecipe() {
2. boilWater();
3. brew();
4. pourInCup();
5. addCondiments();
6. }
void prepareRecipe() {
boilWater();
brew();
pourInCup();
addCondiments();
}
第二步,我们有了新的prepareRecipe方法,但是需要让她能够符合代码,我们先从CaffeineBeverage(咖啡因饮料)超类开始:
Java代码
1. public abstract class CaffeineBeverage {//抽象类
2. //被声明为final,我们不希望子类覆盖这个方法
3. final void prepareRecipe() {
4. boilWater();
5. brew();
6. pourInCup();
7. addCondiments();
8. }
9. //因咖啡和茶处理这些方法的做法不同,所以这2个方法必须被声明为抽象的,剩余的给子类去操心
10. abstract void brew();
11. abstract void addCondiments();
12. //这2个方法不变,不需要子类去处理
13. void boilWater() {
14. System.out.println("Boiling water");
15. }
16. void pourInCup() {
17. System.out.println("Pouring into cup");
18. }
19. }
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来处理冲泡法,所以只需要自行处理冲泡和添加调料部分:
Java代码
1. public class Tea extends CaffeineBeverage {
2. public void brew() {
3. System.out.println("Steeping the tea");
4. }
5. public void addCondiments() {
6. System.out.println("Adding Lemon");
7. }
8. }
9. public class Coffee extends CaffeineBeverage {
10. public void brew() {
11. System.out.println("Dripping Coffee through filter");
12. }
13. public void addCondiments() {
14. System.out.println("Adding Sugar and Milk");
15. }
16. }
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");
}
}
回过头去看看我们刚才做了些什么。基本上,我们刚刚实现的就是模板方法模式,模板方法定义了一个算法的步骤,并允许子类为一个或多个步骤提供实现。
模板方法模式在一个方法中定义了一个算法的骨架,而将一些步骤延迟到子类中。模板方法使得子类可以在不改变算法结构的情况下,重新定义算法中的某些步骤。
钩子是一种被声明在抽象类中的方法,但只有空的或者默认的实现。钩子的存在,可以让子类有能力对算法的不同点进行挂钩,要不要挂钩,由子类自行决定。我们先来看看钩子的用途:
Java代码
1. public abstract class CaffeineBeverageWithHook {
2. void prepareRecipe() {
3. boilWater();
4. brew();
5. pourInCup();
6. //我们加了一个小小的条件语句,而该条件是否成立,是由一个具体方法//customerWantsCondiments决定的。如果顾客“想要”调料时我们才调用addCondiments
7. if (customerWantsCondiments()) {
8. addCondiments();
9. }
10. }
11. abstract void brew();
12. abstract void addCondiments();
13. void boilWater() {System.out.println("Boiling water");}
14. void pourInCup() {System.out.println("Pouring into cup");}
15. //这里定义了一个方法,通常是空的缺省实现。
16. //这个就是一个钩子,子类可以覆盖这个方法,但不见得一定要这么做
17. boolean customerWantsCondiments() {
18. return true;
19. }
20. }
21. //我们如何得知顾客是否想要调料呢?开口问呀。。。
22. public class CoffeeWithHook extends CaffeineBeverageWithHook {
23. public void brew() {
24. System.out.println("Dripping Coffee through filter");
25. }
26. public void addCondiments() {
27. System.out.println("Adding Sugar and Milk");
28. }
29. //我们覆盖了这个钩子,提供自己的功能
30. public boolean customerWantsCondiments() {
31. //让用户输入他们对调料的决定,返回true或false
32. String answer = getUserInput();
33. if (answer.toLowerCase().startsWith("y")) {
34. return true;
35. } else {
36. return false;
37. }
38. }
39. //这个方法询问用户是否想要奶和糖,通过命令行获得用户输入
40. private String getUserInput() {
41. String answer = null;
42. System.out.print("Would you like milk and sugar with your coffee (y/n)? ");
43. BufferedReader in = new BufferedReader(new InputStreamReader(System.in));
44. try {
45. answer = in.readLine();
46. } catch (IOException ioe) {
47. System.err.println("IO error trying to read your answer");
48. }
49. if (answer == null) {
50. return "no";
51. }
52. return answer;
53. }
54. }
55. //好了,我们来测试一下
56. public class BeverageTestDrive {
57. public static void main(String[] args) {
58. //创建一杯咖啡
59. CoffeeWithHook coffeeHook = new CoffeeWithHook();
60. //然后调用prepareRecipe
61. System.out.println("\nMaking coffee...");
62. coffeeHook.prepareRecipe();
63. }
64. }
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();
}
}
我们有一个新的设计原则,称为好莱坞原则:
别调用我们,我们会调用你。
我们允许低层组件将自己挂钩到系统上,但是高层组件会决定什么时候和怎么使用这些低层组件。换句话说。高层组件对待低层组件的方式是“别调用我们,我们会调用你”
本文通过制作咖啡和茶的例子,详细介绍了模板方法模式的概念及其应用。模板方法模式允许子类重新定义算法中的某些步骤,而不改变算法的整体结构。

2510

被折叠的 条评论
为什么被折叠?



