模板方法模式在一个方法中定义一个算法的骨架,而将一些步骤延迟到子类中。模板方法使得子类可以在不改变算法结构的情况下,重新定义算法中的某些步骤。
模板就是一个方法,更具体地说,这个方法将算法定义成一组步骤,其中的任何步骤都可以是抽象的,由子类负责。这可以保证算法的结构保持不变,同时由子类提供部分实现。
将泡茶和冲咖啡的制作步骤通过模板方法模式进行改造
//抽象超类中定义了 模板方法
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 {
//实现超类中的两个抽象方法
@Override
void brew() {
System.out.println("Steeping the tea");
}
@Override
void addCondiments() {
System.out.println("Adding Lemon");
}
}
//子类:咖啡,其中覆盖掉了父类中的钩子方法
public class CoffeeWithHook extends CaffeineBeverage {
@Override
void brew() {
System.out.println("Dripping Coffee through filter");
}
@Override
void addCondiments() {
System.out.println("Adding Sugar and Milk");
}
//覆盖钩子实现自己的方法
@Override
boolean 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 Lemon
Making 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
钩子是一种声明在抽象类中的方法,但只有空的或者默认的实现。钩子的存在,可以让子类有能力对算法的不同点进行挂钩。
钩子的几种用法:
某些步骤是可选的,所以你可以将这些步骤实现成钩子,而不是实现成抽象方法,这样可以让抽象类的子类减轻负荷
低层组件可以参与计算,但是高层组件控制合适以及如何让低层组件参加。低层组件绝对不可以直接调用高层组件。避免让高层和低层组件之间有明显的环形依赖。
迭代器模式提供了一种方法顺序访问一个集合对象的各个元素,而又不暴露其内部的表示。
把游走的任务放在迭代器上,而不是聚合上。这样简化了聚合的接口和实现,也让责任各得其所。
//迭代器接口
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;
}
@Override
public boolean hasNext() {
return position < items.length && items[position] != null;
}
@Override
public 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;
}
@Override
public boolean hasNext() {
return position < items.size() && items.get(position) != null;
}
@Override
public Object next() {
if (hasNext()) {
return items.get(position++);
} else {
return null;
}
}
}
这个原则告诉我们将一个责任只指派给一个类。类的每个字人都有改变的潜在区域。超过一个责任,意味着超过一个改变的区域。
内聚(cohesion)用来衡量一个类或者模块的紧密地达到单一目的或责任。当一个模块或者一个类被设计只支持一组相关的功能时,称之为高内聚;反之,被设计成支持一组不相关的功能时,我们说他具有低内聚。遵守上面这个原则的类具有很高的凝聚力,而且比背负许多责任的内聚类更加容易维护。
组合模式允许你将对象组合成树状结构来表现“整体/部分”层次结构。组合能让客户以一致的方式处理个别对象以及对象组合。
组合模式让我们能用树形方式创建对象的结构,树里面包含了组合以及个别的对象。
使用组合结构,我们能把相同的操作应用在组合和个别对象上,换句话说,在大多数情况下,我们可以忽略对象组合和个别对象之间的差别。
见书 360 页
组合模式以单一责任设计原则换取了透明性(transparency)。通过让组件的接口同时包含一些管理子节点和叶节点的操作,客户就可以将组合和叶节点一视同仁。也就是说,一个元素究竟是组合还是叶节点对客户是透明的。
为了保持透明性,组合内所有的对象都必须实现相同的接口,否则客户就必须操心哪个对象是使用的哪个接口,就失去了组合模式的意义。
状态模式允许对象在内部状态变化时改变它的行为,对象看起来好像修改了它的类
不使用状态模式实现糖果机
//糖果机
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;
}
@Override
public void insertQuarter() {
System.out.println("投入硬币!");
gumballMachine.setState(gumballMachine.getHasQuarterState());
}
@Override
public void ejectQuarter() {
System.out.println("你还没有投入硬币!");
}
@Override
public void turnCrank() {
System.out.println("你转动了曲柄,但你还没有投入硬币!");
}
@Override
public 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;
}
@Override
public void insertQuarter() {
System.out.println("已经投入硬币,无需再投!");
}
@Override
public void ejectQuarter() {
System.out.println("退回硬币!");
gumballMachine.setState(gumballMachine.getNoQuarterState());
}
@Override
public 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());
}
}
@Override
public void dispense() {
System.out.println("无法发放糖果!");
}
}
//售卖状态
public class SoldState implements State{
GumballMachine gumballMachine;
public SoldState(GumballMachine gumballMachine) {
this.gumballMachine = gumballMachine;
}
@Override
public void insertQuarter() {
System.out.println("请等待,糖果就在路上!");
}
@Override
public void ejectQuarter() {
System.out.println("你已经转动曲柄,无法退回!");
}
@Override
public void turnCrank() {
System.out.println("再转一次也只有一颗糖果!");
}
@Override
public 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;
}
@Override
public void insertQuarter() {
System.out.println("请等待,糖果就在路上!");
}
@Override
public void ejectQuarter() {
System.out.println("你已经转动曲柄,无法退回!");
}
@Override
public void turnCrank() {
System.out.println("再转一次也只有一颗糖果!");
}
@Override
public 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;
}
@Override
public void insertQuarter() {
System.out.println("糖果已售罄!");
}
@Override
public void ejectQuarter() {
System.out.println("对不起,你已经转动了曲柄!");
}
@Override
public void turnCrank() {
System.out.println("你转动了曲柄,但糖果已售罄!");
}
@Override
public 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;
}
}