八、模板方法模式

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、迭代器模式类图

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-4kjWXquW-1600240929673)(C:\Users\Ada\AppData\Roaming\Typora\typora-user-images\image-20200914205311631.png)]

4、设计原则

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

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

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

5、组合模式概念

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

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

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

6、组合模式类图

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-qlAs9Vc2-1600240929675)(C:\Users\Ada\AppData\Roaming\Typora\typora-user-images\image-20200915100931912.png)]

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、类图

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-AXoBC4z7-1600240929676)(C:\Users\Ada\AppData\Roaming\Typora\typora-user-images\image-20200915182642897.png)]

4、要点

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

}

void releaseBall() {System.out.println("一颗糖果正在滚出");if (cnt != 0) {cnt--;}
}//填充糖果机
public void refill(int cnt) {this.cnt = cnt;state = noQuarterState;
}

}


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

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

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

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

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

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

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

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

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

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

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

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

  6. Python设计模式(四) -- 模板方法模式

    模板方法模式 定义 定义了一个算法的步骤,并允许子类别为一个或多个步骤提供其实践方式.让子类别在不改变算法架构的情况下,重新定义算法中的某些步骤 适用场景: 事务处理的步骤具有共性,只是具体实施,根据 ...

  7. 深入浅出设计模式四——比萨店生产比萨实现(工厂方法模式)

    在http://quicker.iteye.com/blog/571714一文中已经讲了简单工厂和工厂方法并且有UML图和实现的代码. 这里只结合实例分析一下工厂方法在实际生活中的应用. 我们看看比萨 ...

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

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

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

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

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

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

最新文章

  1. 普元王葱权:数字化时代需要新一代的大数据应用平台架构
  2. 超越GhostNet!吊打MobileNetV3!MicroNet通过极低FLOPs实现图像识别(文末获取论文)
  3. Oracle 与 Mysql NULL值,空字符串''的区别
  4. JavaScript学习笔记(一)--JS基础【入门必看】
  5. joblib 读取模型后对单条数据做预测并解决Reshape your data either using array报错
  6. (52)FPGA面试题-利用函数function实现半字节加法功能(Verilog语言实现)
  7. 帮助用户更好的体验网站:jQuery的页面功能向导插件Pageguide.js
  8. 中国互联网创业大咖(收藏)
  9. 使用Python编写一个QQ聊天机器人
  10. 2021-2027全球及中国数控钻机行业研究及十四五规划分析报告
  11. Java操作excel锁定
  12. 【HNOI2017】大佬-dalao
  13. 在地产行业做安全?也许你该换个专业的企业级DNS
  14. 如何使用Tenderly在Moonbeam上构建更优秀的Dapp
  15. Python Pandas DataFrame 删除缺失值 None Nan drop dropna 替换缺失值 fillna 重复值删除 duplicate 数值替换 replace apply
  16. 华为鸿蒙任正非专访,任正非接受专访:华为鸿蒙系统将比安卓速度快60%
  17. 苹果开发者账号申请流程说明
  18. 华为交换机难点学习:导出配置文件/同步时间
  19. JS实现五星好评效果
  20. EtherCAT介绍

热门文章

  1. HDU 4699(栈)
  2. BZOJ 3097: Hash Killer I【构造题,思维题】
  3. 计算机如何解锁 磁盘,怎么解除Dell电脑硬盘的bitlocker加密
  4. 使用IDO Runtime Development Server跟踪IDO层的错误
  5. webservice 缺少根元素_草莓种植,这2种元素至关重要,直接影响草莓的产量和品质...
  6. 对抗雾霾的健康饮食注意
  7. 节后 威金/Viking 来拜年
  8. java画菱形_JavaSE之绘制菱形
  9. matlab怎么设置步长,请教matlab中怎么对变步长的数据进行处理
  10. 初创公司几个投资人,各占多少股份合适