博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
模板方法模式
阅读量:6316 次
发布时间:2019-06-22

本文共 6236 字,大约阅读时间需要 20 分钟。

hot3.png

模板方法模式

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

这个模式是用来创建一个算法的模板。什么是模板?如你所见,模板就是一个方法。更具体地说,这个方法将算法定义成一组步骤,其中的任何步骤都可以是抽象的,由子类负责实现。这可以确保算法的结构保持不变,同时由子类提供部分实现。

//未改造前: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 {	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");	}}//改造后//咖啡因饮料抽象类public abstract class CaffeineBeverage {	final void prepareRecipe(){		boilWater();		pourInCup();		brew();		addCondiments();	}	void boilWater(){		System.out.println("Boiling water");	}	void pourInCup(){		System.out.println("Pouring into cup");	}	abstract void brew();	abstract void addCondiments();}//改造后public class Coffee extends CaffeineBeverage{	[@Override](https://my.oschina.net/u/1162528)	void brew() {		System.out.println("Dripping Coffee through filter");	}	[@Override](https://my.oschina.net/u/1162528)	void addCondiments() {		System.out.println("Add Sugar and Milk");	}}//改造后public class Tea extends CaffeineBeverage{	[@Override](https://my.oschina.net/u/1162528)	void brew() {		System.out.println("Steeping the tea");	}	[@Override](https://my.oschina.net/u/1162528)	void addCondiments() {		System.out.println("Adding Lemon");	}}

模板方法被声明为final,以免子类改变这个算法的顺序

对模板方法更进一步:

public abstract class AbstractClass {	final void templateMethod(){  //模板方法		primitiveOperation1();		primitiveOperation2();		concreteOperation();		hook();	}	abstract void primitiveOperation1();	abstract void primitiveOperation2();	final void concreteOperation(){		//实现,这个具体的方法被定义在抽象类中。将它声明为final,这样一来子类就无法覆盖它。它可以被模板方法直接使用		//或者被子类使用	}	void hook(){		//我们也可以有"默认不做事的方法",我们称这种方法为"hook"(钩子)。子类可以视情况决定要不要覆盖它们。		//钩子的存在,可以让子类有能力对算法的不同点进行挂钩,由子类自行决定	}}//对模板方法进行挂钩...public abstract class CaffeineBeverageWithHook {	void prepareRecipe(){  //模板方法		boilWater();		brew();		pourInCup();		if (customerWantsCondiments()){			addCondiments();		}	}	abstract void brew();	abstract void addCondiments();	String getUserInput(){		return "no";	}	void boilWater(){		System.out.println("Boiling water");	}	void pourInCup(){		System.out.println("Pouring into cup");	}	boolean customerWantsCondiments(){ //如果实现后面的算法,直接重写改为false		return true;	}}//使用钩子public class CoffeeWithHook extends CaffeineBeverageWithHook {	[@Override](https://my.oschina.net/u/1162528)	void brew() {		System.out.println("Dripping Coffee through filter");	}	@Override	void addCondiments() {		System.out.println("Adding Sugar and Milk");	}	@Override	boolean customerWantsCondiments() {		String answer = getUserInput();		if (answer.toLowerCase().startsWith("y")){			return true;		}else{			return false;		}	}	private String getUserInput() {		String answer = null;		System.out.println("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.out.println("IO error trying to read your answer");		}		if (answer == null) {			return "no";		}		return answer;	}}//测试结果public class BeverageTestDrive {	public static void main(String[] args) {		TeaWithHook teaHook = new TeaWithHook();		CoffeeWithHook coffeeHook = new CoffeeWithHook();		System.out.println("\nMaking tea...");		teaHook.prepareRecipe();		System.out.println("\nMaking coffee...");		coffeeHook.prepareRecipe();	}}

问1:似乎我应该保持抽象方法的数目越少越好,否则,在子类中实现这些方法将会很麻烦。

答案:

  • 当你在写模板方法的时候,心里要随时记得这一点。想要做到这一点,可以让算法内的步骤不要切割得太细,但是如果步骤太少的话,会比较没有弹性,所以要看情况折衷。
  • 也请记住,某些步骤是可选的,所以你可以将这些步骤实现成钩子,而不是实现成抽象方法,这样就可以让抽象类的子类的负荷减轻。

好莱坞原则:

别调用(打电话给)我们,我们会调用(打电话给)你。

好莱坞原则可以给我们一种防止"依赖腐败"的方法。当高层组件依赖底层组件,而底层组件又依赖高层组件,而高层组件又依赖边侧组件,而边侧组件又依赖底层组件时,依赖腐败就发生了。在这种情况下,没有人可以轻易地搞懂系统是如何设计的。

在好莱坞原则之下,我们允许低层组件将自己挂钩到系统上,但是高层组件会决定什么时候和怎样使用这些低层组件。换句话说,高层组件对待低层组件的方式是"别调用我们,我们会调用你"。

重点:高层组件控制何时以及如何让低层组件参与,低层组件可以参与计算,低层组件绝对不可以直接调用高层组件。

问:好莱坞原则和依赖倒置原则之间的关系如何?

答:依赖倒置原则教我们尽量避免使用具体类,而多使用抽象。而好莱坞原则是用在创建框架或组件上的一种技巧,好让低层组件能够被挂钩进计算中,而且又不会让高层组件依赖低层组件。两者的目标都是在于解耦,但是依赖倒置原则更加注重如何在设计中避免依赖。好莱坞原则教我们一个技巧,创建一个有弹性的设计,允许低层结构能够互相操作,而又防止其他类太过依赖它们

API中的模板方法:

Java数组类的设计者提供给我们一个方便的模板方法用来排序。让我们看看这个方法如何运行:

public static void sort(Object[] a){ //Arrays.sort(Object[] objs)	Object aux[] = (Object[])a.clone();	mergeSort(aux,a,0,a.length,0);}private static void mergeSort(Object src[],Object dest[],int low,int high,int off){	for(int i=low;i
low &&((Comparable)dest[j-i]).compareTo((Comparable)dest[j])>0;j--){ swap(dest,j,j-1); } } return;}

我们需要实现compareTo()方法,sort()是静态的,所以使用它和他被定义在超类中是一样的。因为sort()并不是真正定义在超类中, 所以sort()方法需要知道已经实现了这个compartTo()方法,否则就无法进行排序。

//排序鸭子,实现compareTo()方法public class Duck implements Comparable{	String name;	int weight;	public String toString(){		return name + " weighs "+weight;	}	public int compareTo(Object object){		//compareTo()需要被传入另一个鸭子,和本身这只鸭子做比较		Duck otherDuck = (Duck)object;		if(this.weight
otherDuck.weight return 1; } }}//鸭子排序public class DuckSortTestDrive { public static void main(String[] args) { Duck[] ducks = { new Duck("Daffy", 15), new Duck("Mikey", 18), new Duck("TangLaoYa", 17), new Duck("MiNi", 16) }; display(ducks); Arrays.sort(ducks); System.out.println("\n"); display(ducks); } public static void display(Duck[] ducks) { for (Duck duck : ducks) { System.out.println("name:" + duck.getName() + "," + "weight:" + duck.getWeight()); } }}

要点:

  • "模板方法"定义了算法的步骤,把这些步骤的实现延迟到子类。
  • 模板方法模式为我们提供了一种代码复用的重要技巧。
  • 模板方法的抽象类可以定义具体方法、抽象方法和钩子。
  • 抽象方法由子类实现。
  • 钩子是一种方法,它在抽象类中不做事,或者只做默认的事,子类可以选择要不要去覆盖它。
  • 为了防止子类改变模板方法中的算法,可以将模板方法声明为final。
  • 好莱坞原则告诉我们,将决策权放在高层模块中,以便决定如何以及何时调用低层模块。
  • 你将在真实世界代码中看到模板方法模式的许多变体,不要期待它们全都是一眼就可以被你认出的。
  • 策略模式和模板方法模式都封装算法,一个用组合,一个用继承。
  • 工厂方法是模板方法的一种特殊版本。

转载于:https://my.oschina.net/u/3843989/blog/2251667

你可能感兴趣的文章
step-by-step通过数据集成同步数据到HBase
查看>>
斯坦福最新发布首份AI100报告,2030年我们的生活会是什么样子?
查看>>
随机机器学习算法需要试验多少次,才足以客观有效的反映模型性能?
查看>>
大数据风控时代下好车贷等互联网金融平台有哪些特点
查看>>
iOS从0到1搭建高可用App框架
查看>>
如何用深度学习推荐电影?教你做自己的推荐系统!
查看>>
英特尔澄清:第一款10nm产品2017年定发布
查看>>
2016华为在泰国举办全球电力峰会
查看>>
Win 10在2018年达10亿安装量?微软说要再想想
查看>>
WhatsApp全面实施端对端加密 警方无法获取用户信息
查看>>
你还敢开车吗?黑客三分钟攻破你的私家车
查看>>
美国移民浩景投资签约XTools CRM
查看>>
近十年首份数据中心能源情况报告:不要错过这些机会!
查看>>
标签也智能!科学家研发包裹显示屏
查看>>
信息安全,也是一种竞争力
查看>>
Shell脚本防攻击一例
查看>>
NoSQL性能测试白皮书
查看>>
温瑞尔NFV平台加快高效虚拟CPE部署
查看>>
数据库之触发器
查看>>
大国企纷纷盯上“阿里云”,打造中国的“Predix”
查看>>