我在另外一篇博客裏聲稱:作技術的思路是優先「怎麼用」、然後再「是什麼」。然而這裏,我卻想討論一下狀態模式與策略模式「是什麼」,以及它們之間的區別。
這並非打臉,而是我在通過長久的思考「怎麼用」以後,終於明白了它們「是什麼」。git
讓我想明白的契機,是「如何實現狀態機(固然,是有限肯定狀態機)」這樣一個問題。下圖是一個簡單的狀態機:
github
我本來計劃用這樣一套類來實現這個狀態機:
畫好類圖以後我發現,這是個典型的策略模式。可是從直覺上來看,狀態模式纔是狀態機的自然盟友。所以我又嘗試着畫了一套狀態模式的圖:
編程
一眼看上起,這兩張類圖如出一轍。固然,仔細看看,必定能發現其中的區別。相同點會帶來相同的優勢,不一樣之處造就了各自的缺點。安全
這兩張類圖彷佛通常無二:一個對外接口;若干個接口方法;一個基類;若干個子類;另外有一個工廠與之配合。
之因此有如此高的類似度,是由於這兩種模式的基本思路都是同樣的:對外提供一個抽象,對內擴展爲不一樣的實現。app
看起來,只有接口方法不一樣:策略模式用一個接口方法來處理全部的狀態遷移;而狀態模式則用不一樣方法來處理遷移到不一樣狀態時的動做。
這個看起來不起眼的區別,實際上表明瞭這兩種模式大相徑庭的「抽象」方式。策略模式將狀態遷移的「操做」設計爲抽象接口;而狀態模式的抽象接口表明了「狀態」自己。換句話說,策略模式是對行爲的抽象,狀態模式是對數據的抽象。ide
面向接口的設計和編程方式能夠帶來數不盡的有點:接口對內高內聚,對外低耦合;實現類內部高內聚,彼此之間低耦合;對新增實現類保持開放,對修改現有類儘可能封閉;等等等等。spa
用策略模式實現狀態機,從代碼還原到狀態機時會遇到一些困難。在我實踐過的代碼中,團隊成員都反應過這一點。用狀態模式來作會更明白一些:由於狀態模式同時封裝了數據和行爲,雖然也只是一個「片斷」,但片斷中包含的信息更多。
用狀態模式實現,則會遇到實例化的問題。這是因爲包含有數據,因此「狀態」實例線程不安全。於是每次執行狀態遷移操做時,系統都須要使用一個新的實例。固然,這個問題能夠變通解決,可是相比自然只有行爲、一般只須要單例實現的策略模式,這仍是算得上一個缺點的。線程