template method pattern
一个方法中定义一个算法的骨架,而将一些步骤延迟到子类中.
模板方法使得子类可以在不改变算法结构的情况下,重新定义算法中的某些步骤
疑问:
问:创建一个模板方法时,什么时候用抽象方法?什么时候用钩子
答:当你的子类"必须"提供算法中的某个算法或者步骤的实现时,就使用抽象方法
如果算法的这个部分是可选的,就用钩子
如果是钩子的话,子类可以实现这个钩子,但并不强制这么做
问:钩子的真正目的
答:1.钩子可以让子类实现算法中可选的部分 钩子对于子类的实现并不重要,子类可以对钩子置之不理
2.让子类能够有机会对模板方法中某些即将发生的(或者刚刚发生的)步骤做出反应
3.钩子也可以让子类有能力为其抽象类做一些决定
好莱坞原则:
别调用我们,我们会调用你
防止依赖腐败
依赖腐败:
高层组件依赖底层组件,底层组件依赖高层组件,高层组件依赖边侧组件,边侧组件又依赖底层组件,
在好莱坞依赖原则下,允许底层组件将自己挂钩到系统上(IOC就是基于此原则)
/** * 咖啡因饮料 * @author Dougest * 2017年7月4日 下午1:49:52 * */ public abstract class CaffeineBeverageWithHook { void prepareRecipe() { boilWater(); brew();//冲泡 pourInCup(); if(customerWantsCondiments())//钩子 addCondiments();//调料 } abstract void brew(); abstract void addCondiments(); void boilWater(){ int j = 20; System.out.println("烧水...."); for(int i = 20; i < 100;i++) { j+=1; System.out.println("水温: "+(j)+" 度"); } System.out.println("水开了"); } void pourInCup() { System.out.println("到入杯子"); } boolean customerWantsCondiments() { return true; } }
/** * 咖啡 * @author Dougest * 2017年7月4日 下午1:57:17 * */ public class CoffeeWithHook extends CaffeineBeverageWithHook{ void brew() { System.out.println("过滤并倒入该咖啡"); } void addCondiments() { System.out.println("加糖和牛奶"); } /*****************************使用钩子************************************************/ boolean customerWantsCondiments() { String answer = userInput(); return answer.toLowerCase().startsWith("y"); } private String userInput() { 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(Exception e) { e.printStackTrace(); } if(answer == null) return "no"; return answer; } }
public class TeaWithHook extends CaffeineBeverageWithHook{ @Override void brew() { System.out.println("放茶叶"); } @Override void addCondiments() { System.out.println("加糖吗"); } boolean customerWantsCondiments() { String answer = userInput(); return answer.toLowerCase().startsWith("y"); } private String userInput() { String answer = null; System.out.println("Would you like some sugar with your tea (y/n)"); BufferedReader in = new BufferedReader(new InputStreamReader(System.in)); try{ answer = in.readLine(); } catch(Exception e) { e.printStackTrace(); } if(answer == null) return "no"; return answer; } }
public class MainTest { public static void main(String[] args) { CoffeeWithHook ch = new CoffeeWithHook(); ch.prepareRecipe(); TeaWithHook th = new TeaWithHook(); th.prepareRecipe(); } }
在上面的代码中,咖啡因饮料是高层组件,咖啡和茶是底层组件
高层组件控制冲泡的算法,只需要子类在实现某个方法时,才调用子类
底层组件简单的提供一些实现细节
好莱坞原则和依赖倒置原则:
依赖倒置原则教我们尽量避免使用具体类,多使用抽象
好莱坞原则是用在创建框架或者组件上的一种技巧,好让底层组件能够被挂钩进计算中,而有不会被高层组件依赖底层组件
两者的目标都是为了解耦,但是依赖倒置原则更加注重如何在设计中避免依赖
好莱坞原则教我们一个技巧,创建一个有弹性的设计,允许底层结构能够互相操作,又防止其他类太过依赖他们
底层组件在结束时,常常会调用高层组件中继承来的方法
我们要做的是,尽量避免高低层组件之间明显的环状依赖
模板方法是一个很常见的设计模式,对于框架来说;
框架控制如何做事情,由用户指定框架的算法中的每一个步骤的细节
异同点:
模板方法:
1) 定义一个算法的大纲,子类定义某些步骤的内容 算法的不同步骤可以有不同的实现细节,但是算法的结构依然保持不变
2)继承,子类共享重复算法
策略:
1)放弃对算法的控制
2)使用委托,因为对象组合,更有弹性
要点:
定义了算法步骤,把这些步骤延迟到子类
提供了代码复用的一个重要技能
抽象类可以定义具体方法,抽象方法和钩子
抽象方法有子类实现
钩子是一种抽象方法,他在抽象类中不做任何事或者只做默认的事,子类可以选择要不要覆盖他
为了防止子类更改父类中的算法,可以将超类算法用final修饰
好莱坞原则告诉我们,将决策权放在高层模块中,一边决定如何以及何时调用底层模块
策略模式和模板方法都是用来封装算法,不同的是一个是组合,一个是继承
工厂方法是模板方法的一个特殊版本