-
面向对象-设计模式-行为型
简介:面向对象-设计模式-行为型。
一、概述
何谓设计模式:
设计模式(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 输出:
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 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 }