物件導向-設計模式-行為型

濤姐濤哥發表於2021-07-24

物件導向-設計模式-行為型

 

      日暮鄉關何處是?煙波江上使人愁。

 

簡介:物件導向-設計模式-行為型。

一、概述

何謂設計模式

設計模式(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 }
View Code
物件導向-設計模式-行為型
 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 }
View Code
物件導向-設計模式-行為型
 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 }
View Code
物件導向-設計模式-行為型
 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 }
View Code
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 }
View Code
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 }
View Code
物件導向-設計模式-行為型
 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 }
View Code
物件導向-設計模式-行為型
 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 }
View Code
物件導向-設計模式-行為型
 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 }
View Code
 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 }
View Code
物件導向-設計模式-行為型
 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 }
View Code
物件導向-設計模式-行為型
 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 }
View Code
物件導向-設計模式-行為型
 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 }
View Code

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 }
View Code
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 }
View Code
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 }
View Code
物件導向-設計模式-行為型
 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 }
View Code
物件導向-設計模式-行為型
 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 }
View Code
物件導向-設計模式-行為型
 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 }
View Code
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 }
View Code
 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 }
View Code
物件導向-設計模式-行為型
 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 }
View Code
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 }
View Code
物件導向-設計模式-行為型
 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 }
View Code
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 }
View Code
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 }
View Code
物件導向-設計模式-行為型
 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 }
View Code
 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 }
View Code
物件導向-設計模式-行為型
 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 }
View Code
物件導向-設計模式-行為型
 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 }
View Code
物件導向-設計模式-行為型
 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 }
View Code
物件導向-設計模式-行為型
 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 }
View Code
物件導向-設計模式-行為型
 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 }
View Code
 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 }
View Code
 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 }
View Code
物件導向-設計模式-行為型
 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 }
View Code
物件導向-設計模式-行為型
 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 }
View Code
物件導向-設計模式-行為型
 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 }
View Code
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 }
View Code
物件導向-設計模式-行為型
 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 }
View Code
 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 }
View Code

 

 

 

日暮鄉關何處是?

        煙波江上使人愁

 

 

 

相關文章