從狀態模式看 JavaScript 與 Java
這篇文章緣起於前幾天微博上有關動態語言與靜態語言的討論,因為有幾個程式設計高手參加,所以能看到一些特別有啟發性的發言。本文主要是下面這一條微博的讀後感,也是我的練習與思考。
關於動態語言與靜態語言,有很多比較和討論它們的文章,但大部分都沒有抓住重點。而上面一條微博,提到了一個很好的切入點,那就是「狀態模式(State Pattern)」。
狀態模式:蝙蝠俠/布魯斯·韋恩
蝙蝠俠(英語:Batman)是一名出現於DC漫畫的虛構超級英雄角色,由鮑勃·凱恩和比爾·芬格創作。他的名字叫 Bruce,是一位美國億萬富翁,這是他的正常身份,用於正常生活,例如進行參加宴會之類的活動。他的另一個身份是 Batman,是打擊犯罪的黑暗騎士。
這是一個狀態模式的好示例,我用 Java 和 JavaScript 各寫了一個示例,體會體會「極其繁瑣」與「非常簡單」。
極其繁瑣:Java 版本
目錄結構:
com
|-- tianfangye
|-- Client.java
|-- person
|-- Person.java
|-- state
|-- BatmanState.java
|-- BruceState.java
|-- IState.java
Person.java
package com.tianfangye.person;
import com.tianfangye.state.IState;
import com.tianfangye.state.BruceState;
public class Person {
// 當前狀態
private IState state;
public Person() {
this.state = new BruceState();
}
// 設定當前狀態
public void changeState(IState state) {
this.state = state;
}
// 改變狀態(變身)
public void convertState() {
this.state.convertState(this);
}
// 開始行動
public void takeAction() {
this.state.doActivities();
}
}
IState.java
package com.tianfangye.state;
import com.tianfangye.person.Person;
public interface IState {
// 轉換狀態
public void convertState(Person person);
// 執行活動
public void doActivities();
}
BruceState.java
package com.tianfangye.state;
import com.tianfangye.person.Person;
public class BruceState implements IState {
private String name = "- Bruce -";
// 轉換狀態
public void convertState(Person person) {
person.changeState(new BatmanState());
}
// 執行活動
public void doActivities() {
System.out.println(this.name + " <> " + "參加宴會");
}
}
BatmanState.java
package com.tianfangye.state;
import com.tianfangye.person.Person;
public class BatmanState implements IState {
private String name = "- Batman -";
// 轉換狀態
public void convertState(Person person) {
person.changeState(new BruceState());
}
// 執行活動
public void doActivities() {
System.out.println(this.name + " <> " + "打擊犯罪");
}
}
Client.java
package com.tianfangye;
import com.tianfangye.person.Person;
public class Client {
public static void main(String[] args) {
Person person = new Person();
person.takeAction();
person.convertState();
person.takeAction();
}
}
程式輸出:
- Bruce - <> 參加宴會
- Batman - <> 打擊犯罪
關於狀態模式的實現,GoF 的 Design Patterns 裡面提到過一些需要考慮的方面,其中之一是「誰來定義狀態的轉換」。可以由狀態的使用者(Person)實現,也可以由每個狀態各自實現,各有利弊。上例由每個狀態各自實現,接下來的 JavaScript 示例也是這種選擇(對於示例程式,另一種實現更簡單)。
非常簡單:JavaScript 版本
const state = {
_Bruce: {
name: "- Bruce -",
convertState() {
this.identity = state._Batman;
},
takeAction() {
window.console.log(`${this.name} <> 參加宴會`);
}
},
_Batman: {
name: "- Batman -",
convertState() {
this.identity = state._Bruce;
},
takeAction() {
window.console.log(`${this.name} <> 打擊犯罪`);
}
}
};
const person = {
identity: state._Bruce,
convertState() {
this.identity.convertState.call(this);
},
takeAction() {
this.identity.takeAction();
}
};
person.takeAction();
person.convertState();
person.takeAction();
程式輸出:
- Bruce - <> 參加宴會
- Batman - <> 打擊犯罪
可以看到,相比靜態語言的版本,動態語言的版本竟是如此渾然天成!甚至不是在實現「設計模式」,只是對語言特性的正常使用而已。所以,什麼是設計模式?聰明人應該想通了——設計模式是一個衍生問題,不是本質問題。本質問題是程式設計,是靜態語言與動態語言,是靜態型別與動態型別,是程式語言抽象。
@vczh的一個知乎回答對這一點講得很透徹:
顯而易見,型別是個大問題!動態語言能做到非常簡潔是由於動態型別系統的靈活性。它們在相當大的程度上簡化了程度設計,也就是所謂的「非常簡單」。要明白,這裡的「簡單」與「複雜」並不是指程式碼量的多少,也不是指語言特性使用的多少,而是指花費在程式設計上面的心思的多少。具體到本例,主要是下面兩點:
- 動態型別:不需要花心思去搞定型別檢查。
- 執行時繫結上下文的 this 物件:不需要將有狀態的物件傳來傳去。
當然,這些靈活性並非沒有代價(效能)。另外,關於動態語言與靜態語言的選擇,一直有很多工程問題上的爭議(從文章開頭的微博往下探索,可以找到大段這方面的爭論)。我個人對於動態語言、靜態語言沒有明顯偏好,只是喜歡把問題弄清楚——不論有沒有偏好,偏好強烈還是微弱,這都是首要的一步。
相關文章
- javascript設計模式狀態模式JavaScript設計模式
- 《JavaScript設計模式與開發實踐》模式篇(13)—— 狀態模式JavaScript設計模式
- Java設計模式之狀態模式Java設計模式
- JavaScript與有限狀態機JavaScript
- java狀態模式例項解析Java模式
- 折騰Java設計模式之狀態模式Java設計模式
- JAVA設計模式之 狀態模式【State Pattern】Java設計模式
- 狀態模式模式
- 從任務中心看狀態機功能元件設計元件
- MFC的模組狀態:從AfxGetApp()和AFX_MANAGE_STATE()看MFC的模組狀態APP
- JavaStatePattern(狀態模式)JavaAST模式
- JS 狀態模式JS模式
- (三)狀態模式模式
- 狀態模式(State)模式
- Javascript 設計模式系統講解與應用——學習筆記10-狀態模式JavaScript設計模式筆記
- 設計模式-狀態模式設計模式
- 設計模式:狀態模式設計模式
- 策略模式與狀態模式不是雙胞胎,而是情侶!模式
- 從工作流狀態機實踐中總結狀態模式使用心得模式
- JavaScript狀態資料JavaScript
- 行為型模式:狀態模式模式
- 設計模式(十五)狀態模式設計模式
- 設計模式之——狀態模式設計模式
- 設計模式(六):狀態模式設計模式
- 狀態變化模式模式
- 狀態模式(State pattern)模式
- 17_狀態模式模式
- 檢視看防火牆狀態防火牆
- 用設計模式去掉沒必要的狀態變數 —— 狀態模式設計模式變數
- 狀態機模式 與 ajax 的結合運用模式
- PHP 設計模式之狀態模式PHP設計模式
- 簡說設計模式——狀態模式設計模式
- 設計模式之狀態模式(State)設計模式
- 設計模式-狀態模式(State Pattern)設計模式
- python設計模式狀態模式Python設計模式
- 設計模式20之狀態模式設計模式
- 極簡設計模式-狀態模式設計模式
- GoLang設計模式14 - 狀態模式Golang設計模式