八、模板方法模式

1、概念

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

模板就是一个方法,更具体地说,这个方法将算法定义成一组步骤,其中的任何步骤都可以是抽象的,由子类负责。这可以保证算法的结构保持不变,同时由子类提供部分实现。

2、例子

将泡茶和冲咖啡的制作步骤通过模板方法模式进行改造

//抽象超类中定义了 模板方法
public abstract class CaffeineBeverage {//模板方法中定义了算法final void prepareRecipe(){boilWater();brew();pourInCup();//利用钩子实现根据客户要求是否添加调料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 Tea extends CaffeineBeverage {//实现超类中的两个抽象方法@Overridevoid brew() {System.out.println("Steeping the tea");}@Overridevoid addCondiments() {System.out.println("Adding Lemon");}
}//子类:咖啡,其中覆盖掉了父类中的钩子方法
public class CoffeeWithHook extends CaffeineBeverage {@Overridevoid brew() {System.out.println("Dripping Coffee through filter");}@Overridevoid addCondiments() {System.out.println("Adding Sugar and Milk");}//覆盖钩子实现自己的方法@Overrideboolean customerWantsCondiments() {String ans = getUserInput();if (ans.toLowerCase().startsWith("y")) {return true;} else {return false;}}//读取用户是否需要在咖啡中加入调料private String getUserInput() {String ans = null;System.out.print("Would you like milk and suger with your coffee (y/n)? ");BufferedReader in = new BufferedReader(new InputStreamReader(System.in));try {ans = in.readLine();} catch (IOException e) {e.printStackTrace();}if (ans == null) {ans = "no";}return ans;}
}//测试类
public class CaffeineBeverageTest {public static void main(String[] args) {CaffeineBeverage tea = new Tea();CaffeineBeverage coffeeWithHook = new CoffeeWithHook();System.out.println("\nMaking tea.........");tea.prepareRecipe();System.out.println("\nMaking coffee.........");coffeeWithHook.prepareRecipe();}
}//测试结果
Making tea.........
Boiling water
Steeping the tea
Pouring into cup
Adding LemonMaking coffee.........
Boiling water
Dripping Coffee through filter
Pouring into cup
Would you like milk and suger with your coffee (y/n)? Yes  --->咖啡中钩子实现的方法
Adding Sugar and Milk

3、关于钩子

钩子是一种声明在抽象类中的方法,但只有空的或者默认的实现。钩子的存在,可以让子类有能力对算法的不同点进行挂钩。

钩子的几种用法:

  • 钩子可以让子类实现算法中的可选部分,或者在钩子对于子类的实现并不重要的时候,子类可以对此钩子置之不理。
  • 让子类能够有机会对模板方法中某些即将发生的(或者刚刚发生的)步骤作出反应。例如名为 justReOrderList() 的钩子方法允许子类在内部列表重新组织后执行某些动作(如在屏幕上重新显示数据)。

某些步骤是可选的,所以你可以将这些步骤实现成钩子,而不是实现成抽象方法,这样可以让抽象类的子类减轻负荷

4、设计原则

  • 别调用我们,我们会调用你:由超类主控一切,当它们需要的时候,自然会去调用子类

低层组件可以参与计算,但是高层组件控制合适以及如何让低层组件参加。低层组件绝对不可以直接调用高层组件。避免让高层和低层组件之间有明显的环形依赖。

5、要点

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

九、迭代器和组合模式

1、迭代器模式概念

迭代器模式提供了一种方法顺序访问一个集合对象的各个元素,而又不暴露其内部的表示。

把游走的任务放在迭代器上,而不是聚合上。这样简化了聚合的接口和实现,也让责任各得其所。

2、自定义迭代器例子

//迭代器接口
public interface Iterator {boolean hasNext();Object next();}//实体类
public class MenuItem {}//午餐项迭代器
public class DinerMenuIterator implements Iterator {MenuItem[] items;int position = 0;public DinerMenuIterator(MenuItem[] items) {this.items = items;}@Overridepublic boolean hasNext() {return position < items.length && items[position] != null;}@Overridepublic Object next() {if (hasNext()) {return items[position++];} else {return null;}}
}//煎饼迭代器
public class PancakeHouseIterator implements Iterator{ArrayList<MenuItem> items;int position = 0;public PancakeHouseIterator(ArrayList<MenuItem> items) {this.items = items;}@Overridepublic boolean hasNext() {return position < items.size() && items.get(position) != null;}@Overridepublic Object next() {if (hasNext()) {return items.get(position++);} else {return null;}}
}

3、迭代器模式类图

4、设计原则

  • 一个类应该只有一个引起变化的原因

这个原则告诉我们将一个责任只指派给一个类。类的每个字人都有改变的潜在区域。超过一个责任,意味着超过一个改变的区域。

内聚(cohesion)用来衡量一个类或者模块的紧密地达到单一目的或责任。当一个模块或者一个类被设计只支持一组相关的功能时,称之为高内聚;反之,被设计成支持一组不相关的功能时,我们说他具有低内聚。遵守上面这个原则的类具有很高的凝聚力,而且比背负许多责任的内聚类更加容易维护。

5、组合模式概念

组合模式允许你将对象组合成树状结构来表现“整体/部分”层次结构。组合能让客户以一致的方式处理个别对象以及对象组合。

组合模式让我们能用树形方式创建对象的结构,树里面包含了组合以及个别的对象。

使用组合结构,我们能把相同的操作应用在组合和个别对象上,换句话说,在大多数情况下,我们可以忽略对象组合和个别对象之间的差别。

6、组合模式类图

7、组合模式例子

见书 360 页

8、组合模式和单一责任设计原则

组合模式以单一责任设计原则换取了透明性(transparency)。通过让组件的接口同时包含一些管理子节点和叶节点的操作,客户就可以将组合和叶节点一视同仁。也就是说,一个元素究竟是组合还是叶节点对客户是透明的。

为了保持透明性,组合内所有的对象都必须实现相同的接口,否则客户就必须操心哪个对象是使用的哪个接口,就失去了组合模式的意义。

9、要点

  • 得带起允许访问聚合的元素,而不需要暴露它的内部结构
  • 迭代器将遍历聚合的工作封装到一个对象中
  • 当使用迭代器的时候,我们依赖聚合提供遍历
  • 迭代器提供了一个通用的接口,让我们遍历聚合的项,当我们编码使用聚合的项时,就可以使用多态机制
  • 应该努力让一个类只分配一个责任
  • 组合模式提供了一个结构,可同时包容个别对象和组合对象
  • 组合模式允许客户对个别对象和组合对象一视同仁
  • 组合构造内部的任何对象称为组件,组件可以是组合,也可以是叶节点
  • 在实现组合模式时,有许多设计上的折衷。你要根据需要平衡透明性和安全性。

十、状态模式

1、概念

状态模式允许对象在内部状态变化时改变它的行为,对象看起来好像修改了它的类

2、例子

不使用状态模式实现糖果机

//糖果机
public class GumballMachineOld {final static int SOLD_OUT = 0;final static int NO_QUARTER = 1;final static int HAS_QUARTER = 2;final static int SOLD = 3;int state = SOLD_OUT; //记录当前状态int cnt = 0; //当前糖果数量public GumballMachineOld(int cnt) {this.cnt = cnt;if (cnt > 0) {state = NO_QUARTER;}}//对于投入硬币,不同状态的反应public void insertQuarter() {if (state == HAS_QUARTER) {System.out.println("已经投入硬币,无需再投!");} else if (state == NO_QUARTER) {state = HAS_QUARTER;System.out.println("投入硬币!");} else if (state == SOLD_OUT) {System.out.println("糖果已售罄!");} else if (state == SOLD) {System.out.println("请等待,糖果就在路上!");}}//对于退回硬币,不同状态的反应public void ejectQuarter() {if (state == HAS_QUARTER) {System.out.println("退回硬币!");state = NO_QUARTER;} else if (state == NO_QUARTER) {System.out.println("你还没有投入硬币!");} else if (state == SOLD_OUT) {System.out.println("对不起,你已经转动了曲柄!");} else if (state == SOLD) {System.out.println("没有硬币投入,无法退回!");}}//对于转动曲柄,不同状态的反应public void turnCrank() {if (state == HAS_QUARTER) {System.out.println("你转动了曲柄,正在放出糖果!");state = SOLD;dispense();} else if (state == NO_QUARTER) {System.out.println("你转动了曲柄,但你还没有投入硬币!");} else if (state == SOLD_OUT) {System.out.println("你转动了曲柄,但糖果已售罄!");} else if (state == SOLD) {System.out.println("再转一次也只有一颗糖果!");}}//放出糖果private void dispense() {if (state == SOLD) {System.out.println("一颗糖果滚了出来!");cnt--;if (cnt == 0) {System.out.println("糖果买完了!");state = SOLD_OUT;} else {state = NO_QUARTER;}} else if (state == NO_QUARTER) {System.out.println("你需要先投入硬币!");} else if (state == SOLD_OUT) {System.out.println("无法发放糖果!");} else if (state == HAS_QUARTER) {System.out.println("无法发放糖果!");}}
}

使用状态模式实现糖果机,并添加中奖游戏(有10%的机会得到两颗糖果)

//状态接口,其中定义了可能会的动作(请求)
public interface State {void insertQuarter();void ejectQuarter();void turnCrank();void dispense();
}//没有硬币状态
public class NoQuarterState implements State{GumballMachine gumballMachine;public NoQuarterState(GumballMachine gumballMachine) {this.gumballMachine = gumballMachine;}@Overridepublic void insertQuarter() {System.out.println("投入硬币!");gumballMachine.setState(gumballMachine.getHasQuarterState());}@Overridepublic void ejectQuarter() {System.out.println("你还没有投入硬币!");}@Overridepublic void turnCrank() {System.out.println("你转动了曲柄,但你还没有投入硬币!");}@Overridepublic void dispense() {System.out.println("你需要先投入硬币!");}
}//有硬币状态
public class HasQuarterState implements State{//随机数生成生成Random randomWinner = new Random(System.currentTimeMillis());GumballMachine gumballMachine;public HasQuarterState(GumballMachine gumballMachine) {this.gumballMachine = gumballMachine;}@Overridepublic void insertQuarter() {System.out.println("已经投入硬币,无需再投!");}@Overridepublic void ejectQuarter() {System.out.println("退回硬币!");gumballMachine.setState(gumballMachine.getNoQuarterState());}@Overridepublic void turnCrank() {System.out.println("你转动了曲柄,正在放出糖果!");int winner = randomWinner.nextInt(10);if (winner == 0 && (gumballMachine.getCnt() > 1)) {gumballMachine.setState(gumballMachine.getWinnerState());} else {gumballMachine.setState(gumballMachine.getSoldState());}}@Overridepublic void dispense() {System.out.println("无法发放糖果!");}
}//售卖状态
public class SoldState implements State{GumballMachine gumballMachine;public SoldState(GumballMachine gumballMachine) {this.gumballMachine = gumballMachine;}@Overridepublic void insertQuarter() {System.out.println("请等待,糖果就在路上!");}@Overridepublic void ejectQuarter() {System.out.println("你已经转动曲柄,无法退回!");}@Overridepublic void turnCrank() {System.out.println("再转一次也只有一颗糖果!");}@Overridepublic void dispense() {//先放出糖果gumballMachine.releaseBall();//再根据是否还有糖果进行状态设置if (gumballMachine.getCnt() > 0) {gumballMachine.setState(gumballMachine.getNoQuarterState());} else {System.out.println("糖果买完了!");gumballMachine.setState(gumballMachine.getSoldOutState());}}
}//赢得两颗糖果状态类
public class WinnerState implements State{GumballMachine gumballMachine;public WinnerState(GumballMachine gumballMachine) {this.gumballMachine = gumballMachine;}@Overridepublic void insertQuarter() {System.out.println("请等待,糖果就在路上!");}@Overridepublic void ejectQuarter() {System.out.println("你已经转动曲柄,无法退回!");}@Overridepublic void turnCrank() {System.out.println("再转一次也只有一颗糖果!");}@Overridepublic void dispense() {//先放出一糖果System.out.println("你中奖了,你将能得到两颗糖果!");gumballMachine.releaseBall();if (gumballMachine.getCnt() == 0) {//再根据是否还有糖果System.out.println("糖果买完了!");gumballMachine.setState(gumballMachine.getSoldOutState());} else {gumballMachine.releaseBall();if(gumballMachine.getCnt() == 0) {gumballMachine.setState(gumballMachine.getSoldOutState());} else {System.out.println("糖果买完了!");gumballMachine.setState(gumballMachine.getNoQuarterState());}}}
}//售罄状态
public class SoldOutState implements State{GumballMachine gumballMachine;public SoldOutState(GumballMachine gumballMachine) {this.gumballMachine = gumballMachine;}@Overridepublic void insertQuarter() {System.out.println("糖果已售罄!");}@Overridepublic void ejectQuarter() {System.out.println("对不起,你已经转动了曲柄!");}@Overridepublic void turnCrank() {System.out.println("你转动了曲柄,但糖果已售罄!");}@Overridepublic void dispense() {System.out.println("已售罄,无法发放糖果!");}
}//使用状态模式的糖果机
public class GumballMachine {//所有的状态State soldOutState;State soldState;State noQuarterState;State hasQuarterState;State winnerState;State state = soldOutState;int cnt = 0; //当前糖果数量public GumballMachine(int cnt) {soldOutState = new SoldOutState(this);soldState = new SoldState(this);noQuarterState = new NoQuarterState(this);hasQuarterState = new HasQuarterState(this);winnerState = new WinnerState(this);this.cnt = cnt;if (cnt > 0) {state = noQuarterState;}}public void insertQuarter() {state.insertQuarter();}public void ejectQuarter() {state.ejectQuarter();}public void turnCrank() {state.turnCrank();state.dispense();}void setState(State state) {this.state = state;}void releaseBall() {System.out.println("一颗糖果正在滚出");if (cnt != 0) {cnt--;}}//填充糖果机public void refill(int cnt) {this.cnt = cnt;state = noQuarterState;}
}

3、类图

4、要点

  • 状态模式允许一个对象基于内部状态而拥有不同的行为
  • 和程序状态机(PSM)不同,状态模式用类表示状态
  • Context会将行为委托给当前的状态对象
  • 通过将每个状态封装到一个类,我们把以后需要做的任何改变局部化了
  • 状态模式和策略模式有相同的类图,但他们的意图不同
  • 策略模式通常会用行为或者算法来配置Context类
  • 状态模式允许Context随着状态的改变而改变行为。
  • 状态转换可以由state类或者Context类控制
  • 使用状态模式通常会导致设计中类的数量大量增加
  • 状态类可以被多个Context实例共享

设计模式(三):模板方法模式、迭代器和组合模式、状态模式相关推荐

  1. 设计模式(三):“花瓶+鲜花”中的装饰者模式(Decorator Pattern)

    在前两篇博客中详细的介绍了"策略模式"和"观察者模式",今天我们就通过花瓶与鲜花的例子来类比一下"装饰模式"(Decorator Patte ...

  2. C#常用设计模式(Unity)——游戏场景的转换——状态模式(State)

    此文章原文来源于<设计模式与完美游戏开发>(蔡升达著),笔者只是在学习过程中受益颇多,从而进行了总结,有兴趣的读者可以去阅读原书. 1.场景的转换 当游戏比较复杂的时候,通常会设计多个场景 ...

  3. 设计模式第十二次作业——观察者模式、状态模式

    组件协作模式:现代软件专业分工之后的第一个结果是"框架与应用程序的划分","组件协作"模式通过晚期绑定,来实现框架与 应用程序之间的松耦合,是二者之间协作时常用 ...

  4. 设计模式与泡mm的关系之state状态模式及再思考

    我跑我跑我跑 网上原文如下: 20.State,跟mm交往时,一定要注意她的状态哦,在不同的状态时她的行为会有不同,比如你约她今天晚上去看电影,对你没兴趣的mm就会说"有事情啦", ...

  5. 状态模式java 在线投票_Java 状态模式

    在阎宏博士的<JAVA与模式>一书中开头是这样描述状态(State)模式的:状态模式,又称状态对象模式(Pattern of Objects for States),状态模式是对象的行为模 ...

  6. 测试类图Head First 设计模式 (九) 迭代器与组合模式(Iterator Composite pattern) C++实现...

    在改章节中,我们主要介绍测试类图的内容,自我感觉有个不错的建议和大家分享下 迭代器模式供提一种方法序顺问访一个聚合对象中的各个素元,而又不露暴其部内的示表. 计设准则:   单一任责准则:一个类应当只 ...

  7. 7、大话设计模式--状态模式 、适配器模式、备忘录模式、组合模式、迭代器模式

    第十六章:无尽加班何时休--状态模式 状态模式 :   优点: 缺点: 所使用的项目是:工作状态 功能: 界面: 设计思路: 收获: 附: 1.概述 在软件开发过程中,应用程序可能会根据不同的情况作出 ...

  8. 每天一个设计模式之模板方法模式(Template Method Pattern)

    所谓的模板模式就是基类(抽象类)提供出定义好的一个模板(空实现+默认实现),子类按照模板封装好的顺序去填充模板内方法的实现. 一.UML类图 图中,templateMethod是对子类暴露出的方法,它 ...

  9. 设计模式之模板方法模式(Template Method)摘录

    23种GOF设计模式一般分为三大类:创建型模式.结构型模式.行为模式. 创建型模式抽象了实例化过程,它们帮助一个系统独立于如何创建.组合和表示它的那些对象.一个类创建型模式使用继承改变被实例化的类,而 ...

  10. 设计模式之状态模式(State)摘录

    23种GOF设计模式一般分为三大类:创建型模式.结构型模式.行为模式. 创建型模式抽象了实例化过程,它们帮助一个系统独立于如何创建.组合和表示它的那些对象.一个类创建型模式使用继承改变被实例化的类,而 ...

最新文章

  1. R语言计算回归模型每个样本(观察、observation、sample)的杠杆值(leverage)实战:如果一个样本的预测变量比其他样本的预测变量值更极端,那么被认为具有很高的杠杆作用
  2. 核试验计算机模拟,随着信息技术的发展,包括核试验在内的许多科学研究都可以用计算机模拟...
  3. SQLServer 2012 报表服务部署配置(1)
  4. LiveVideoStack线上交流分享 (十) —— 开源声码器WORLD在语音合成中的应用
  5. 湖南工业职业技术学院计算机协会,计算机网络协会
  6. python requests模块中返回时间elapsed解析
  7. cad插件_CAD插件乱刀去教育戳记安装教程
  8. 【手记】解决Intel Management Engine Interface黄色感叹号
  9. smart原则_OKR 文化:用 SMART 原则量化目标
  10. 图片加载失败替代文字_替代艺术:为图像编写出色的描述性文字
  11. PHP开发宝典-PHP基础
  12. springboot 配置文件 date-format 失效
  13. 艺赛旗(RPA)新手课堂 - 原始字符串为什么不能以反斜杠结尾
  14. greenDao小坑一个
  15. 云计算系统测试之技术概念
  16. Cauchy–Schwarz inequality理解
  17. 2018-2019(1)教学随笔
  18. 通达信l2接口是什么意思?
  19. ChatGPT研究报告:AIGC带来新一轮范式转移
  20. 陈力:传智播客古代 珍宝币 泡泡龙游戏开发第53讲:PHP smarty模板配置及变量操作

热门文章

  1. 概率统计相关基础知识
  2. 罗永浩:成都分公司没倒闭 今年是锤子很艰辛的一年
  3. Vijos 1028-魔族密码【暴力】
  4. Android实现仿360手机卫士悬浮窗效果
  5. 666-集群聊天服务器项目总结
  6. oracle 透明网关 MSSQL(oracle database gateway)
  7. MySQL+JAVA 员工考勤管理系统 数据库大作业
  8. SQL DATEPART()函数
  9. ChatGPT正式开放「上网能力」!
  10. Transformer——注意力模型分析及应用