2013-02-06 90 views
0

說我們有一個(英國)交通燈模擬應用和交通燈都有相關的有限狀態機定義爲類: -有限狀態機可以在不破壞FSM封裝的情況下使用持久性嗎?

* --> RED --> RED_AMBER --> GREEN --> AMBER --> RED --> ... 
(repeat until the proverbial cows make an appearance) 

在建設交通燈的狀態是RED 幾樣時間觸發導致狀態改變。

在應用程序中可能存在像(除去從點帶走任何代碼)一些代碼...

TrafficLight trafficLightsAtBigJunction = new TrafficLight(); // state = RED 
trafficLightsAtBigJunction.setState(TrafficLightState.RED_AMBER); 
trafficLightsAtBigJunction.setState(TrafficLightState.GREEN); 
trafficLightsAtBigJunction.setState(TrafficLightState.AMBER); 
trafficLightsAtBigJunction.setState(TrafficLightState.RED); 
trafficLightsAtBigJunction.setState(TrafficLightState.RED_AMBER); 
: 
: 
: 

的癥結在於,使用狀態模式來實現狀態機,如果我們這樣做

TrafficLight trafficLightsAtBigJunction = new TrafficLight(); // state = RED 
trafficLightsAtBigJunction.setState(TrafficLightState.GREEN); // Exception!!!!! 

由於它是非法的狀態移動而引發異常(通過我們的設計)。這就是我們想要的。世界上的一切都很好。

但是,如果我們繼續堅持交通信號燈,而且恰好處於state = AMBER的話,那麼就會出現問題。當我們的用戶在3天后回來觀看真棒交通燈模擬時,它會從一些(關心)持久性商店中的當前狀態恢復。

我們如何讓紅綠燈實例處於狀態AMBER而不破壞狀態模式在這裏提供的封裝?

似乎有2個選擇: - (1)創建一個新的實例並運行通過相關國家 (2)提供一個特殊的方法設置狀態,無論我們想的是,按照慣例,僅在從某個持久性存儲中讀取後才能使用。例如與

trafficLight.setStateAfterReadingFromPersistanceSource(AMBER); 

問題(1)我看到的是,有很可能的副作用經歷的狀態運行時,我不想,加上邏輯可以根據不同的狀態機是相當複雜

問題與(2)顯然它只是按照慣例工作,所以可能引入錯誤而不知道何時被錯誤地使用。更重要的是,它幾乎打破了所有你最好的模式封裝,這是你想要的東西。

的問題是持續性的技術無關 - 與ORM,文件化,系列化等同樣的問題

我假設這裏有一個解決方案,但我想不出自己一個和我的谷歌搜索技巧都不足以。

任何指針都會很棒。

回答

1

的方式我看到它,你希望兩個方法來操作狀態:

1)從這種狀態過渡到另一種狀態,進行這種轉變的所有的副作用,如果非法拋出異常等

2)將機器直接設置爲一個狀態/一組內部值。別的什麼都別做。

你應該堅持描述FSM內部狀態的所有內容,並且有兩種方法,一種是前者,另一種是後者。

後者將用於設置時或者當非操作時。編寫代碼也很簡單,因爲它只是將值轉換成變量,而不用擔心還需要發生什麼。

前者將在模擬過程中使用。

+0

是的,所以我想這是一個更簡潔的方式來提出我的問題。就我所見,似乎是一種二分法。你有你的「正常」封裝流程,但由於持久維度需要某種「走出去」。 –

1

最簡單的方法可能只是將初始狀態作爲構造函數參數傳遞 - 只是您的慣例,系統以所有燈開始爲紅色。

另一種方法是製作將商店中的數據拉成朋友或成員的函數(取決於您是使用操作符>>來閱讀它還是其他)。這使您可以選擇按照您的示例轉換到某個狀態,或從商店讀取初始狀態。關於發生的事情沒有多少含糊之處,取決於FSM在堅持時將其狀態以及需要的任何其他內容從商店中取出。

+0

對不起,是的,對於構造函數,我的意思是包含它作爲選項的一部分。我會用特殊的方法將它括起來,僅在從保存狀態構建時使用。類似的缺點,它允許錯誤在其他地方在有能力創建TrafficLights的代碼中蔓延。就RED而言,我確實同意這一點,但我選擇簡化的案例來說明我的觀點。我打算明確的是,FSM定義了起始狀態,流程和結束。持久性似乎阻礙了它。 –

+0

@AndyBoyle如果某件東西有能力創造出某種東西,它通常會完全控制。也許你需要一個環境構建器和一個模擬器 - FSM實現了模擬器看到的一個轉換接口,允許它執行狀態轉換,但是環境創建了可以串行化或膨脹的具體對象。 –

1

對於簡短的回答,我同意皮特,在這個簡單的例子中,你可以通過它作爲 構造函數參數。

但我真的認爲整個設計是有缺陷的。我認爲這應該使用標準的狀態設計模式來建模。事情是這樣的:

class TrafficLight 
{ 
    private TrafficLightState _lightState; 

    TrafficLight(initialState) 
    { 
     // utilize lookup table or factory-method to assign _lightState with the correct TrafficLightState subclass 
    } 

    // UI can use this to identify/render the appropriate color 
    Color getColorCode() 
    { 
     return _lightState.getColorCode(); 
    } 

    // UI uses this to know when to signal the next light change (each color can have different duration) 
    int getDuration() 
    { 
     return _lightState.getDuration(); 
    } 

    // assuming the UI has a timer that is set based on the current light's duration 
    void changeLight() 
    { 
     TrafficLightState nextState = _lightState.onChangeLight(); 
     _lightState = nextState; 
    } 

} 

abstract class TrafficLightState 
{ 
    abstract Color getColorCode() 
    abstract TrafficLightState onChangeLight() 
    abstract int getDuration() 
} 


class RedLight : TrafficLightState 
{ 

    Color getColorCode() 
    { 
     return Color.Red; 
    } 

    TrafficLightState onChangeLight() 
    { 
     return new RedAmberLight(); 
    } 

    int getDuration() 
    { 
     return 30; 
    } 
} 

class RedAmberLight : TrafficLightState 
{ 

    Color getColorCode() 
    { 
     return Color.Orange; 
    } 

    TrafficLightState onChangeLight() 
    { 
     return new GreenLight(); 
    } 

    int getDuration() 
    { 
     return 10; 
    } 
} 

class GreenLight: TrafficLightState 
{ 

    Color getColorCode() 
    { 
     return Color.Green; 
    } 

    TrafficLightState onChangeLight() 
    { 
     return new AmberLight(); 
    } 

    int getDuration() 
    { 
     return 25; 
    } 
} 

class AmberLight: TrafficLightState 
{ 

    Color getColorCode() 
    { 
     return Color.Yellow; 
    } 

    TrafficLightState onChangeLight() 
    { 
     return new RedLight(); 
    } 

    int getDuration() 
    { 
     return 10; 
    } 
} 

國家機器不應該被用來在正常操作轉變的明確暴露的「改變狀態」的方法。相反,將它們視爲具有允許狀態機轉換自己狀態的刺激。在這個例子中,刺激非常簡單,但通常你會有一堆可能導致狀態轉換的輸入。但是,通過適當的封裝,呼叫者不需要過度地意識到細節。

+0

有趣的點。總體而言,我並不反對,建議的實施是可靠的。但我想我的問題可能更具理論性,因爲我認爲不管它是否是用戶界面,某些客戶端或內部代碼,最終將FSM移動到所需的狀態都不重要。重點在於,仍然有兩種選擇可以允許直接設置「以某種方式」或通過可能具有副作用的狀態。我確實認爲客戶應該從內部狀態變化中抽象出來,但是「某些事物」需要將它們移動到期望的狀態 –

+1

在那種情況下,我不認爲他們可以是單個答案,因爲它取決於什麼狀態機正在執行。對於像紅綠燈這樣的東西,當然可以跳到一個狀態。但是我已經使用狀態機來處理,例如,tcp/ip協議,並且您不能在未進入發送請求狀態的情況下跳轉到接收 - 響應狀態。 – tcarvin

0

通過將狀態和轉換表示爲對象來實現狀態機當然是可能的,但這些對象需要初始化(這似乎是您的問題)並且帶上了寶貴的RAM。

但是,還有一種完全不同的方式將狀態機實現爲純代碼。這有很多優點,我永遠不會回到「狀態機作爲數據」方法。

對於一個具體的例子,http://www.drdobbs.com/architecture-and-design/uml-statecharts-at-1099/188101799的DDJ文章「UML Statecharts at $ 10.99」顯示瞭如何實現行人光控(PELICAN)穿越作爲分級狀態機。

本例使用C語言編寫,用於低端微控制器。如果你對C++實現感興趣,你可以看看SourceForge.net提供的開源QP/C++框架https://sourceforge.net/projects/qpc/files/QP_C%2B%2B/

相關問題