面向对象-设计模式-行为型
日暮乡关何处是?烟波江上使人愁。
简介:面向对象-设计模式-行为型。
一、概述
何谓设计模式:
设计模式(Design Pattern)是一套被反复使用、多数人知晓的、经过分类的、代码设计经验的总结。
设计模式的好处&学习目的:
1、为了代码可重用行、让代码更易被他人理解、保证代码的可靠性、使代码编写真正实现工程化;
2、设计模式便于我们维护项目,增强系统的健壮性和可扩展性;
3、设计模式还可以锻炼码农的设计思维、升华代码质量等。
二、行为型
责任链模式、命令模式、解释器模式、迭代器模式、中介者模式、备忘录模式、观察者模式、状态模式、策略模式、模板方法模式、访问者模式、空对象模式。
1. 责任链(Chain Of Responsibility)
Intent
使多个对象都有机会处理请求,从而避免请求的发送者和接收者之间的耦合关系。将这些对象连成一条链,并沿着这条链发送该请求,直到有一个对象处理它为止。
Class Diagram
- Handler:定义处理请求的接口,并且实现后继链(successor)
Implementation

1 public abstract class Handler { 2
3 protected Handler successor; 4
5
6 public Handler(Handler successor) { 7 this.successor = successor; 8 } 9
10
11 protected abstract void handleRequest(Request request); 12 }

1 public class ConcreteHandler1 extends Handler { 2
3 public ConcreteHandler1(Handler successor) { 4 super(successor); 5 } 6
7
8 @Override 9 protected void handleRequest(Request request) { 10 if (request.getType() == RequestType.TYPE1) { 11 System.out.println(request.getName() + " is handle by ConcreteHandler1"); 12 return; 13 } 14 if (successor != null) { 15 successor.handleRequest(request); 16 } 17 } 18 }

1 public class ConcreteHandler2 extends Handler { 2
3 public ConcreteHandler2(Handler successor) { 4 super(successor); 5 } 6
7
8 @Override 9 protected void handleRequest(Request request) { 10 if (request.getType() == RequestType.TYPE2) { 11 System.out.println(request.getName() + " is handle by ConcreteHandler2"); 12 return; 13 } 14 if (successor != null) { 15 successor.handleRequest(request); 16 } 17 } 18 }

1 public class Request { 2
3 private RequestType type; 4 private String name; 5
6
7 public Request(RequestType type, String name) { 8 this.type = type; 9 this.name = name; 10 } 11
12
13 public RequestType getType() { 14 return type; 15 } 16
17
18 public String getName() { 19 return name; 20 } 21 }
1 public enum RequestType { 2 TYPE1, TYPE2 3 }

1 public class Client { 2
3 public static void main(String[] args) { 4
5 Handler handler1 = new ConcreteHandler1(null); 6 Handler handler2 = new ConcreteHandler2(handler1); 7
8 Request request1 = new Request(RequestType.TYPE1, "request1"); 9 handler2.handleRequest(request1); 10
11 Request request2 = new Request(RequestType.TYPE2, "request2"); 12 handler2.handleRequest(request2); 13 } 14 }
1 输出: 2 request1 is handle by ConcreteHandler1 3 request2 is handle by ConcreteHandler2
2. 命令(Command)
Intent
将命令封装成对象中,具有以下作用:
- 使用命令来参数化其它对象
- 将命令放入队列中进行排队
- 将命令的操作记录到日志中
- 支持可撤销的操作
Class Diagram
- Command:命令
- Receiver:命令接收者,也就是命令真正的执行者
- Invoker:通过它来调用命令
- Client:可以设置命令与命令的接收者
Implementation
设计一个遥控器,可以控制电灯开关。
1 public interface Command { 2 void execute(); 3 }

1 public class LightOnCommand implements Command { 2 Light light; 3
4 public LightOnCommand(Light light) { 5 this.light = light; 6 } 7
8 @Override 9 public void execute() { 10 light.on(); 11 } 12 }

1 public class LightOffCommand implements Command { 2 Light light; 3
4 public LightOffCommand(Light light) { 5 this.light = light; 6 } 7
8 @Override 9 public void execute() { 10 light.off(); 11 } 12 }

1 public class Light { 2
3 public void on() { 4 System.out.println("Light is on!"); 5 } 6
7 public void off() { 8 System.out.println("Light is off!"); 9 } 10 }

1 /**
2 * 遥控器 3 */
4 public class Invoker { 5 private Command[] onCommands; 6 private Command[] offCommands; 7 private final int slotNum = 7; 8
9 public Invoker() { 10 this.onCommands = new Command[slotNum]; 11 this.offCommands = new Command[slotNum]; 12 } 13
14 public void setOnCommand(Command command, int slot) { 15 onCommands[slot] = command; 16 } 17
18 public void setOffCommand(Command command, int slot) { 19 offCommands[slot] = command; 20 } 21
22 public void onButtonWasPushed(int slot) { 23 onCommands[slot].execute(); 24 } 25
26 public void offButtonWasPushed(int slot) { 27 offCommands[slot].execute(); 28 } 29 }
1 public class Client { 2 public static void main(String[] args) { 3 Invoker invoker = new Invoker(); 4 Light light = new Light(); 5 Command lightOnCommand = new LightOnCommand(light); 6 Command lightOffCommand = new LightOffCommand(light); 7 invoker.setOnCommand(lightOnCommand, 0); 8 invoker.setOffCommand(lightOffCommand, 0); 9 invoker.onButtonWasPushed(0); 10 invoker.offButtonWasPushed(0); 11 } 12 }
3. 解释器(Interpreter)
Intent
为语言创建解释器,通常由语言的语法和语法分析来定义。
Class Diagram
- TerminalExpression:终结符表达式,每个终结符都需要一个 TerminalExpression。
- Context:上下文,包含解释器之外的一些全局信息。
Implementation
以下是一个规则检验器实现,具有 and 和 or 规则,通过规则可以构建一颗解析树,用来检验一个文本是否满足解析树定义的规则。
例如一颗解析树为 D And (A Or (B C)),文本 "D A" 满足该解析树定义的规则。
这里的 Context 指的是 String。
1 public abstract class Expression { 2 public abstract boolean interpret(String str); 3 }

1 public class TerminalExpression extends Expression { 2
3 private String literal = null; 4
5 public TerminalExpression(String str) { 6 literal = str; 7 } 8
9 public boolean interpret(String str) { 10 StringTokenizer st = new StringTokenizer(str); 11 while (st.hasMoreTokens()) { 12 String test = st.nextToken(); 13 if (test.equals(literal)) { 14 return true; 15 } 16 } 17 return false; 18 } 19 }

1 public class AndExpression extends Expression { 2
3 private Expression expression1 = null; 4 private Expression expression2 = null; 5
6 public AndExpression(Expression expression1, Expression expression2) { 7 this.expression1 = expression1; 8 this.expression2 = expression2; 9 } 10
11 public boolean interpret(String str) { 12 return expression1.interpret(str) && expression2.interpret(str); 13 } 14 }

1 public class OrExpression extends Expression { 2 private Expression expression1 = null; 3 private Expression expression2 = null; 4
5 public OrExpression(Expression expression1, Expression expression2) { 6 this.expression1 = expression1; 7 this.expression2 = expression2; 8 } 9
10 public boolean interpret(String str) { 11 return expression1.interpret(str) || expression2.interpret(str); 12 } 13 }

1 public class Client { 2
3 /**
4 * 构建解析树 5 */
6 public static Expression buildInterpreterTree() { 7 // Literal
8 Expression terminal1 = new TerminalExpression("A"); 9 Expression terminal2 = new TerminalExpression("B"); 10 Expression terminal3 = new TerminalExpression("C"); 11 Expression terminal4 = new TerminalExpression("D"); 12 // B C
13 Expression alternation1 = new OrExpression(terminal2, terminal3); 14 // A Or (B C)
15 Expression alternation2 = new OrExpression(terminal1, alternation1); 16 // D And (A Or (B C))
17 return new AndExpression(terminal4, alternation2); 18 } 19
20 public static void main(String[] args) { 21 Expression define = buildInterpreterTree(); 22 String context1 = "D A"; 23 String context2 = "A B"; 24 System.out.println(define.interpret(context1)); // true
25 System.out.println(define.interpret(context2)); // false
26 } 27 }
4. 迭代器(Iterator)
Intent
提供一种顺序访问聚合对象元素的方法,并且不暴露聚合对象的内部表示。
Class Diagram
- Aggregate 是聚合类,其中 createIterator() 方法可以产生一个 Iterator;
- Iterator 主要定义了 hasNext() 和 next() 方法;
- Client 组合了 Aggregate,为了迭代遍历 Aggregate,也需要组合 Iterator。
Implementation
1 public interface Aggregate { 2 Iterator createIterator(); 3 }

1 public class ConcreteAggregate implements Aggregate { 2
3 private Integer[] items; 4
5 public ConcreteAggregate() { 6 items = new Integer[10]; 7 for (int i = 0; i < items.length; i++) { 8 items[i] = i; 9 } 10 } 11
12 @Override 13 public Iterator createIterator() { 14 return new ConcreteIterator<Integer>(items); 15 } 16 }
1 public interface Iterator<Item> { 2 Item next(); 3 boolean hasNext(); 4 }

1 public class ConcreteIterator<Item> implements Iterator { 2
3 private Item[] items; 4 private int position = 0; 5
6 public ConcreteIterator(Item[] items) { 7 this.items = items; 8 } 9
10 @Override 11 public Object next() { 12 return items[position++]; 13 } 14
15 @Override 16 public boolean hasNext() { 17 return position < items.length; 18 } 19 }
1 public class Client { 2 public static void main(String[] args) { 3 Aggregate aggregate = new ConcreteAggregate(); 4 Iterator<Integer> iterator = aggregate.createIterator(); 5 while (iterator.hasNext()) { 6 System.out.println(iterator.next()); 7 } 8 } 9 }
5. 中介者(Mediator)
Intent
集中相关对象之间复杂的沟通和控制方式。
Class Diagram
- Mediator:中介者,定义一个接口用于与各同事(Colleague)对象通信。
- Colleague:同事,相关对象。
Implementation
Alarm(闹钟)、CoffeePot(咖啡壶)、Calendar(日历)、Sprinkler(喷头)是一组相关的对象,在某个对象的事件产生时需要去操作其它对象,形成了下面这种依赖结构:
使用中介者模式可以将复杂的依赖结构变成星形结构:
1 public abstract class Colleague { 2 public abstract void onEvent(Mediator mediator); 3 }

1 public class Alarm extends Colleague { 2
3 @Override 4 public void onEvent(Mediator mediator) { 5 mediator.doEvent("alarm"); 6 } 7
8 public void doAlarm() { 9 System.out.println("doAlarm()"); 10 } 11 }

1 public class CoffeePot extends Colleague { 2 @Override 3 public void onEvent(Mediator mediator) { 4 mediator.doEvent("coffeePot"); 5 } 6
7 public void doCoffeePot() { 8 System.out.println("doCoffeePot()"); 9 } 10 }

1 public class Calender extends Colleague { 2 @Override 3 public void onEvent(Mediator mediator) { 4 mediator.doEvent("calender"); 5 } 6
7 public void doCalender() { 8 System.out.println("doCalender()"); 9 } 10 }

1 public class Sprinkler extends Colleague { 2 @Override 3 public void onEvent(Mediator mediator) { 4 mediator.doEvent("sprinkler"); 5 } 6
7 public void doSprinkler() { 8 System.out.println("doSprinkler()"); 9 } 10 }
1 public abstract class Mediator { 2 public abstract void doEvent(String eventType); 3 }

1 public class ConcreteMediator extends Mediator { 2 private Alarm alarm; 3 private CoffeePot coffeePot; 4 private Calender calender; 5 private Sprinkler sprinkler; 6
7 public ConcreteMediator(Alarm alarm, CoffeePot coffeePot, Calender calender, Sprinkler sprinkler) { 8 this.alarm = alarm; 9 this.coffeePot = coffeePot; 10 this.calender = calender; 11 this.sprinkler = sprinkler; 12 } 13
14 @Override 15 public void doEvent(String eventType) { 16 switch (eventType) { 17 case "alarm": 18 doAlarmEvent(); 19 break; 20 case "coffeePot": 21 doCoffeePotEvent(); 22 break; 23 case "calender": 24 doCalenderEvent(); 25 break; 26 default: 27 doSprinklerEvent(); 28 } 29 } 30
31 public void doAlarmEvent() { 32 alarm.doAlarm(); 33 coffeePot.doCoffeePot(); 34 calender.doCalender(); 35 sprinkler.doSprinkler(); 36 } 37
38 public void doCoffeePotEvent() { 39 // ...
40 } 41
42 public void doCalenderEvent() { 43 // ...
44 } 45
46 public void doSprinklerEvent() { 47 // ...
48 } 49 }
1 public class Client { 2 public static void main(String[] args) { 3 Alarm alarm = new Alarm(); 4 CoffeePot coffeePot = new CoffeePot(); 5 Calender calender = new Calender(); 6 Sprinkler sprinkler = new Sprinkler(); 7 Mediator mediator = new ConcreteMediator(alarm, coffeePot, calender, sprinkler); 8 // 闹钟事件到达,调用中介者就可以操作相关对象
9 alarm.onEvent(mediator); 10 } 11 }
1 输出: 2 doAlarm() 3 doCoffeePot() 4 doCalender() 5 doSprinkler()
6. 备忘录(Memento)
Intent
在不违反封装的情况下获得对象的内部状态,从而在需要时可以将对象恢复到最初状态。
Class Diagram
- Originator:原始对象
- Caretaker:负责保存好备忘录
- Memento:备忘录,存储原始对象的状态。备忘录实际上有两个接口,一个是提供给 Caretaker 的窄接口:它只能将备忘录传递给其它对象;一个是提供给 Originator 的宽接口,允许它访问到先前状态所需的所有数据。理想情况是只允许 Originator 访问本备忘录的内部状态。
Implementation
以下实现了一个简单计算器程序,可以输入两个值,然后计算这两个值的和。备忘录模式允许将这两个值存储起来,然后在某个时刻用存储的状态进行恢复。

1 /**
2 * Originator Interface 3 */
4 public interface Calculator { 5
6 // Create Memento
7 PreviousCalculationToCareTaker backupLastCalculation(); 8
9 // setMemento
10 void restorePreviousCalculation(PreviousCalculationToCareTaker memento); 11
12 int getCalculationResult(); 13
14 void setFirstNumber(int firstNumber); 15
16 void setSecondNumber(int secondNumber); 17 }

1 /**
2 * Originator Implementation 3 */
4 public class CalculatorImp implements Calculator { 5
6 private int firstNumber; 7 private int secondNumber; 8
9 @Override 10 public PreviousCalculationToCareTaker backupLastCalculation() { 11 // create a memento object used for restoring two numbers
12 return new PreviousCalculationImp(firstNumber, secondNumber); 13 } 14
15 @Override 16 public void restorePreviousCalculation(PreviousCalculationToCareTaker memento) { 17 this.firstNumber = ((PreviousCalculationToOriginator) memento).getFirstNumber(); 18 this.secondNumber = ((PreviousCalculationToOriginator) memento).getSecondNumber(); 19 } 20
21 @Override 22 public int getCalculationResult() { 23 // result is adding two numbers
24 return firstNumber + secondNumber; 25 } 26
27 @Override 28 public void setFirstNumber(int firstNumber) { 29 this.firstNumber = firstNumber; 30 } 31
32 @Override 33 public void setSecondNumber(int secondNumber) { 34 this.secondNumber = secondNumber; 35 } 36 }
1 /**
2 * Memento Interface to Originator 3 * 4 * This interface allows the originator to restore its state 5 */
6 public interface PreviousCalculationToOriginator { 7 int getFirstNumber(); 8 int getSecondNumber(); 9 }
1 /**
2 * Memento interface to CalculatorOperator (Caretaker) 3 */
4 public interface PreviousCalculationToCareTaker { 5 // no operations permitted for the caretaker
6 }

1 /**
2 * Memento Object Implementation 3 * <p> 4 * Note that this object implements both interfaces to Originator and CareTaker 5 */
6 public class PreviousCalculationImp implements PreviousCalculationToCareTaker, 7 PreviousCalculationToOriginator { 8
9 private int firstNumber; 10 private int secondNumber; 11
12 public PreviousCalculationImp(int firstNumber, int secondNumber) { 13 this.firstNumber = firstNumber; 14 this.secondNumber = secondNumber; 15 } 16
17 @Override 18 public int getFirstNumber() { 19 return firstNumber; 20 } 21
22 @Override 23 public int getSecondNumber() { 24 return secondNumber; 25 } 26 }

1 /**
2 * CareTaker object 3 */
4 public class Client { 5
6 public static void main(String[] args) { 7 // program starts
8 Calculator calculator = new CalculatorImp(); 9
10 // assume user enters two numbers
11 calculator.setFirstNumber(10); 12 calculator.setSecondNumber(100); 13
14 // find result
15 System.out.println(calculator.getCalculationResult()); 16
17 // Store result of this calculation in case of error
18 PreviousCalculationToCareTaker memento = calculator.backupLastCalculation(); 19
20 // user enters a number
21 calculator.setFirstNumber(17); 22
23 // user enters a wrong second number and calculates result
24 calculator.setSecondNumber(-290); 25
26 // calculate result
27 System.out.println(calculator.getCalculationResult()); 28
29 // user hits CTRL + Z to undo last operation and see last result
30 calculator.restorePreviousCalculation(memento); 31
32 // result restored
33 System.out.println(calculator.getCalculationResult()); 34 } 35 }
1 输出: 2 110
3 -273
4 110
7. 观察者(Observer)
Intent
定义对象之间的一对多依赖,当一个对象状态改变时,它的所有依赖都会收到通知并且自动更新状态。
主题(Subject)是被观察的对象,而其所有依赖者(Observer)称为观察者。
Class Diagram
主题(Subject)具有注册和移除观察者、并通知所有观察者的功能,主题是通过维护一张观察者列表来实现这些操作的。
观察者(Observer)的注册功能需要调用主题的 registerObserver() 方法。
Implementation
天气数据布告板会在天气信息发生改变时更新其内容,布告板有多个,并且在将来会继续增加。
1 public interface Subject { 2 void registerObserver(Observer o); 3
4 void removeObserver(Observer o); 5
6 void notifyObserver(); 7 }

1 public class WeatherData implements Subject { 2 private List<Observer> observers; 3 private float temperature; 4 private float humidity; 5 private float pressure; 6
7 public WeatherData() { 8 observers = new ArrayList<>(); 9 } 10
11 public void setMeasurements(float temperature, float humidity, float pressure) { 12 this.temperature = temperature; 13 this.humidity = humidity; 14 this.pressure = pressure; 15 notifyObserver(); 16 } 17
18 @Override 19 public void registerObserver(Observer o) { 20 observers.add(o); 21 } 22
23 @Override 24 public void removeObserver(Observer o) { 25 int i = observers.indexOf(o); 26 if (i >= 0) { 27 observers.remove(i); 28 } 29 } 30
31 @Override 32 public void notifyObserver() { 33 for (Observer o : observers) { 34 o.update(temperature, humidity, pressure); 35 } 36 } 37 }
1 public interface Observer { 2 void update(float temp, float humidity, float pressure); 3 }

1 public class StatisticsDisplay implements Observer { 2
3 public StatisticsDisplay(Subject weatherData) { 4 weatherData.registerObserver(this); 5 } 6
7 @Override 8 public void update(float temp, float humidity, float pressure) { 9 System.out.println("StatisticsDisplay.update: " + temp + " " + humidity + " " + pressure); 10 } 11 }

1 public class CurrentConditionsDisplay implements Observer { 2
3 public CurrentConditionsDisplay(Subject weatherData) { 4 weatherData.registerObserver(this); 5 } 6
7 @Override 8 public void update(float temp, float humidity, float pressure) { 9 System.out.println("CurrentConditionsDisplay.update: " + temp + " " + humidity + " " + pressure); 10 } 11 }
1 public class WeatherStation { 2 public static void main(String[] args) { 3 WeatherData weatherData = new WeatherData(); 4 CurrentConditionsDisplay currentConditionsDisplay = new CurrentConditionsDisplay(weatherData); 5 StatisticsDisplay statisticsDisplay = new StatisticsDisplay(weatherData); 6
7 weatherData.setMeasurements(0, 0, 0); 8 weatherData.setMeasurements(1, 1, 1); 9 } 10 }
1 输出: 2 CurrentConditionsDisplay.update: 0.0 0.0 0.0
3 StatisticsDisplay.update: 0.0 0.0 0.0
4 CurrentConditionsDisplay.update: 1.0 1.0 1.0
5 StatisticsDisplay.update: 1.0 1.0 1.0
8. 状态(State)
Intent
允许对象在内部状态改变时改变它的行为,对象看起来好像修改了它所属的类。
Class Diagram
Implementation
糖果销售机有多种状态,每种状态下销售机有不同的行为,状态可以发生转移,使得销售机的行为也发生改变。
1 public interface State { 2 /**
3 * 投入 25 分钱 4 */
5 void insertQuarter(); 6
7 /**
8 * 退回 25 分钱 9 */
10 void ejectQuarter(); 11
12 /**
13 * 转动曲柄 14 */
15 void turnCrank(); 16
17 /**
18 * 发放糖果 19 */
20 void dispense(); 21 }

1 public class HasQuarterState implements State { 2
3 private GumballMachine gumballMachine; 4
5 public HasQuarterState(GumballMachine gumballMachine) { 6 this.gumballMachine = gumballMachine; 7 } 8
9 @Override 10 public void insertQuarter() { 11 System.out.println("You can't insert another quarter"); 12 } 13
14 @Override 15 public void ejectQuarter() { 16 System.out.println("Quarter returned"); 17 gumballMachine.setState(gumballMachine.getNoQuarterState()); 18 } 19
20 @Override 21 public void turnCrank() { 22 System.out.println("You turned..."); 23 gumballMachine.setState(gumballMachine.getSoldState()); 24 } 25
26 @Override 27 public void dispense() { 28 System.out.println("No gumball dispensed"); 29 } 30 }

1 public class NoQuarterState implements State { 2
3 GumballMachine gumballMachine; 4
5 public NoQuarterState(GumballMachine gumballMachine) { 6 this.gumballMachine = gumballMachine; 7 } 8
9 @Override 10 public void insertQuarter() { 11 System.out.println("You insert a quarter"); 12 gumballMachine.setState(gumballMachine.getHasQuarterState()); 13 } 14
15 @Override 16 public void ejectQuarter() { 17 System.out.println("You haven't insert a quarter"); 18 } 19
20 @Override 21 public void turnCrank() { 22 System.out.println("You turned, but there's no quarter"); 23 } 24
25 @Override 26 public void dispense() { 27 System.out.println("You need to pay first"); 28 } 29 }

1 public class SoldOutState implements State { 2
3 GumballMachine gumballMachine; 4
5 public SoldOutState(GumballMachine gumballMachine) { 6 this.gumballMachine = gumballMachine; 7 } 8
9 @Override 10 public void insertQuarter() { 11 System.out.println("You can't insert a quarter, the machine is sold out"); 12 } 13
14 @Override 15 public void ejectQuarter() { 16 System.out.println("You can't eject, you haven't inserted a quarter yet"); 17 } 18
19 @Override 20 public void turnCrank() { 21 System.out.println("You turned, but there are no gumballs"); 22 } 23
24 @Override 25 public void dispense() { 26 System.out.println("No gumball dispensed"); 27 } 28 }

1 public class SoldState implements State { 2
3 GumballMachine gumballMachine; 4
5 public SoldState(GumballMachine gumballMachine) { 6 this.gumballMachine = gumballMachine; 7 } 8
9 @Override 10 public void insertQuarter() { 11 System.out.println("Please wait, we're already giving you a gumball"); 12 } 13
14 @Override 15 public void ejectQuarter() { 16 System.out.println("Sorry, you already turned the crank"); 17 } 18
19 @Override 20 public void turnCrank() { 21 System.out.println("Turning twice doesn't get you another gumball!"); 22 } 23
24 @Override 25 public void dispense() { 26 gumballMachine.releaseBall(); 27 if (gumballMachine.getCount() > 0) { 28 gumballMachine.setState(gumballMachine.getNoQuarterState()); 29 } else { 30 System.out.println("Oops, out of gumballs"); 31 gumballMachine.setState(gumballMachine.getSoldOutState()); 32 } 33 } 34 }

1 public class GumballMachine { 2
3 private State soldOutState; 4 private State noQuarterState; 5 private State hasQuarterState; 6 private State soldState; 7
8 private State state; 9 private int count = 0; 10
11 public GumballMachine(int numberGumballs) { 12 count = numberGumballs; 13 soldOutState = new SoldOutState(this); 14 noQuarterState = new NoQuarterState(this); 15 hasQuarterState = new HasQuarterState(this); 16 soldState = new SoldState(this); 17
18 if (numberGumballs > 0) { 19 state = noQuarterState; 20 } else { 21 state = soldOutState; 22 } 23 } 24
25 public void insertQuarter() { 26 state.insertQuarter(); 27 } 28
29 public void ejectQuarter() { 30 state.ejectQuarter(); 31 } 32
33 public void turnCrank() { 34 state.turnCrank(); 35 state.dispense(); 36 } 37
38 public void setState(State state) { 39 this.state = state; 40 } 41
42 public void releaseBall() { 43 System.out.println("A gumball comes rolling out the slot..."); 44 if (count != 0) { 45 count -= 1; 46 } 47 } 48
49 public State getSoldOutState() { 50 return soldOutState; 51 } 52
53 public State getNoQuarterState() { 54 return noQuarterState; 55 } 56
57 public State getHasQuarterState() { 58 return hasQuarterState; 59 } 60
61 public State getSoldState() { 62 return soldState; 63 } 64
65 public int getCount() { 66 return count; 67 } 68 }

1 public class Client { 2
3 public static void main(String[] args) { 4 GumballMachine gumballMachine = new GumballMachine(5); 5
6 gumballMachine.insertQuarter(); 7 gumballMachine.turnCrank(); 8
9 gumballMachine.insertQuarter(); 10 gumballMachine.ejectQuarter(); 11 gumballMachine.turnCrank(); 12
13 gumballMachine.insertQuarter(); 14 gumballMachine.turnCrank(); 15 gumballMachine.insertQuarter(); 16 gumballMachine.turnCrank(); 17 gumballMachine.ejectQuarter(); 18
19 gumballMachine.insertQuarter(); 20 gumballMachine.insertQuarter(); 21 gumballMachine.turnCrank(); 22 gumballMachine.insertQuarter(); 23 gumballMachine.turnCrank(); 24 gumballMachine.insertQuarter(); 25 gumballMachine.turnCrank(); 26 } 27 }
1 输出: 2 You insert a quarter 3 You turned... 4 A gumball comes rolling out the slot... 5 You insert a quarter 6 Quarter returned 7 You turned, but there's no quarter
8 You need to pay first 9 You insert a quarter 10 You turned... 11 A gumball comes rolling out the slot... 12 You insert a quarter 13 You turned... 14 A gumball comes rolling out the slot... 15 You haven't insert a quarter
16 You insert a quarter 17 You can't insert another quarter
18 You turned... 19 A gumball comes rolling out the slot... 20 You insert a quarter 21 You turned... 22 A gumball comes rolling out the slot... 23 Oops, out of gumballs 24 You can't insert a quarter, the machine is sold out
25 You turned, but there are no gumballs 26 No gumball dispensed
9. 策略(Strategy)
Intent
定义一系列算法,封装每个算法,并使它们可以互换。
策略模式可以让算法独立于使用它的客户端。
Class Diagram
- Strategy 接口定义了一个算法族,它们都实现了 behavior() 方法。
- Context 是使用到该算法族的类,其中的 doSomething() 方法会调用 behavior(),setStrategy(Strategy) 方法可以动态地改变 strategy 对象,也就是说能动态地改变 Context 所使用的算法。
与状态模式的比较
状态模式的类图和策略模式类似,并且都是能够动态改变对象的行为。但是状态模式是通过状态转移来改变 Context 所组合的 State 对象,而策略模式是通过 Context 本身的决策来改变组合的 Strategy 对象。所谓的状态转移,是指 Context 在运行过程中由于一些条件发生改变而使得 State 对象发生改变,注意必须要是在运行过程中。
状态模式主要是用来解决状态转移的问题,当状态发生转移了,那么 Context 对象就会改变它的行为;而策略模式主要是用来封装一组可以互相替代的算法族,并且可以根据需要动态地去替换 Context 使用的算法。
Implementation
设计一个鸭子,它可以动态地改变叫声。这里的算法族是鸭子的叫声行为。
1 public interface QuackBehavior { 2 void quack(); 3 }
1 public class Quack implements QuackBehavior { 2 @Override 3 public void quack() { 4 System.out.println("quack!"); 5 } 6 }
1 public class Squeak implements QuackBehavior{ 2 @Override 3 public void quack() { 4 System.out.println("squeak!"); 5 } 6 }

1 public class Duck { 2
3 private QuackBehavior quackBehavior; 4
5 public void performQuack() { 6 if (quackBehavior != null) { 7 quackBehavior.quack(); 8 } 9 } 10
11 public void setQuackBehavior(QuackBehavior quackBehavior) { 12 this.quackBehavior = quackBehavior; 13 } 14 }
1 public class Client { 2
3 public static void main(String[] args) { 4 Duck duck = new Duck(); 5 duck.setQuackBehavior(new Squeak()); 6 duck.performQuack(); 7 duck.setQuackBehavior(new Quack()); 8 duck.performQuack(); 9 } 10 }
1 输出: 2 squeak!
3 quack!
10. 模板方法(Template Method)
Intent
定义算法框架,并将一些步骤的实现延迟到子类。
通过模板方法,子类可以重新定义算法的某些步骤,而不用改变算法的结构。
Class Diagram
Implementation
冲咖啡和冲茶都有类似的流程,但是某些步骤会有点不一样,要求复用那些相同步骤的代码。
1 public abstract class CaffeineBeverage { 2
3 final void prepareRecipe() { 4 boilWater(); 5 brew(); 6 pourInCup(); 7 addCondiments(); 8 } 9
10 abstract void brew(); 11
12 abstract void addCondiments(); 13
14 void boilWater() { 15 System.out.println("boilWater"); 16 } 17
18 void pourInCup() { 19 System.out.println("pourInCup"); 20 } 21 }
1 public class Coffee extends CaffeineBeverage { 2 @Override 3 void brew() { 4 System.out.println("Coffee.brew"); 5 } 6
7 @Override 8 void addCondiments() { 9 System.out.println("Coffee.addCondiments"); 10 } 11 }
1 public class Tea extends CaffeineBeverage { 2 @Override 3 void brew() { 4 System.out.println("Tea.brew"); 5 } 6
7 @Override 8 void addCondiments() { 9 System.out.println("Tea.addCondiments"); 10 } 11 }
1 public class Client { 2 public static void main(String[] args) { 3 CaffeineBeverage caffeineBeverage = new Coffee(); 4 caffeineBeverage.prepareRecipe(); 5 System.out.println("-----------"); 6 caffeineBeverage = new Tea(); 7 caffeineBeverage.prepareRecipe(); 8 } 9 }
1 输出: 2 boilWater 3 Coffee.brew 4 pourInCup 5 Coffee.addCondiments 6 -----------
7 boilWater 8 Tea.brew 9 pourInCup 10 Tea.addCondiments
11. 访问者(Visitor)
Intent
为一个对象结构(比如组合结构)增加新能力。
Class Diagram
- Visitor:访问者,为每一个 ConcreteElement 声明一个 visit 操作
- ConcreteVisitor:具体访问者,存储遍历过程中的累计结果
- ObjectStructure:对象结构,可以是组合结构,或者是一个集合。
Implementation
1 public interface Element { 2 void accept(Visitor visitor); 3 }

1 class CustomerGroup { 2
3 private List<Customer> customers = new ArrayList<>(); 4
5 void accept(Visitor visitor) { 6 for (Customer customer : customers) { 7 customer.accept(visitor); 8 } 9 } 10
11 void addCustomer(Customer customer) { 12 customers.add(customer); 13 } 14 }

1 public class Customer implements Element { 2
3 private String name; 4 private List<Order> orders = new ArrayList<>(); 5
6 Customer(String name) { 7 this.name = name; 8 } 9
10 String getName() { 11 return name; 12 } 13
14 void addOrder(Order order) { 15 orders.add(order); 16 } 17
18 public void accept(Visitor visitor) { 19 visitor.visit(this); 20 for (Order order : orders) { 21 order.accept(visitor); 22 } 23 } 24 }

1 public class Order implements Element { 2
3 private String name; 4 private List<Item> items = new ArrayList(); 5
6 Order(String name) { 7 this.name = name; 8 } 9
10 Order(String name, String itemName) { 11 this.name = name; 12 this.addItem(new Item(itemName)); 13 } 14
15 String getName() { 16 return name; 17 } 18
19 void addItem(Item item) { 20 items.add(item); 21 } 22
23 public void accept(Visitor visitor) { 24 visitor.visit(this); 25
26 for (Item item : items) { 27 item.accept(visitor); 28 } 29 } 30 }

1 public class Item implements Element { 2
3 private String name; 4
5 Item(String name) { 6 this.name = name; 7 } 8
9 String getName() { 10 return name; 11 } 12
13 public void accept(Visitor visitor) { 14 visitor.visit(this); 15 } 16 }
1 public interface Visitor { 2 void visit(Customer customer); 3
4 void visit(Order order); 5
6 void visit(Item item); 7 }

1 public class GeneralReport implements Visitor { 2
3 private int customersNo; 4 private int ordersNo; 5 private int itemsNo; 6
7 public void visit(Customer customer) { 8 System.out.println(customer.getName()); 9 customersNo++; 10 } 11
12 public void visit(Order order) { 13 System.out.println(order.getName()); 14 ordersNo++; 15 } 16
17 public void visit(Item item) { 18 System.out.println(item.getName()); 19 itemsNo++; 20 } 21
22 public void displayResults() { 23 System.out.println("Number of customers: " + customersNo); 24 System.out.println("Number of orders: " + ordersNo); 25 System.out.println("Number of items: " + itemsNo); 26 } 27 }

1 public class Client { 2 public static void main(String[] args) { 3 Customer customer1 = new Customer("customer1"); 4 customer1.addOrder(new Order("order1", "item1")); 5 customer1.addOrder(new Order("order2", "item1")); 6 customer1.addOrder(new Order("order3", "item1")); 7
8 Order order = new Order("order_a"); 9 order.addItem(new Item("item_a1")); 10 order.addItem(new Item("item_a2")); 11 order.addItem(new Item("item_a3")); 12 Customer customer2 = new Customer("customer2"); 13 customer2.addOrder(order); 14
15 CustomerGroup customers = new CustomerGroup(); 16 customers.addCustomer(customer1); 17 customers.addCustomer(customer2); 18
19 GeneralReport visitor = new GeneralReport(); 20 customers.accept(visitor); 21 visitor.displayResults(); 22 } 23 }
1 输出: 2 customer1 3 order1 4 item1 5 order2 6 item1 7 order3 8 item1 9 customer2 10 order_a 11 item_a1 12 item_a2 13 item_a3 14 Number of customers: 2
15 Number of orders: 4
16 Number of items: 6
12. 空对象(Null)
Intent
使用什么都不做的空对象来代替 NULL。
一个方法返回 NULL,意味着方法的调用端需要去检查返回值是否是 NULL,这么做会导致非常多的冗余的检查代码。并且如果某一个调用端忘记了做这个检查返回值,而直接使用返回的对象,那么就有可能抛出空指针异常。
Class Diagram
Implementation
1 public abstract class AbstractOperation { 2 abstract void request(); 3 }
1 public class RealOperation extends AbstractOperation { 2 @Override 3 void request() { 4 System.out.println("do something"); 5 } 6 }
1 public class NullOperation extends AbstractOperation{ 2 @Override 3 void request() { 4 // do nothing
5 } 6 }

1 public class Client { 2 public static void main(String[] args) { 3 AbstractOperation abstractOperation = func(-1); 4 abstractOperation.request(); 5 } 6
7 public static AbstractOperation func(int para) { 8 if (para < 0) { 9 return new NullOperation(); 10 } 11 return new RealOperation(); 12 } 13 }
日暮乡关何处是?
烟波江上使人愁