考慮我有一個的gen_fsm實現的FSM。對於某個StateName中的某個事件,我應該將數據寫入數據庫並回複用戶結果。所以下面Statename的由函數表示:OTP原理。如何在實踐中分離功能和非功能的代碼?
statename(Event, _From, StateData) when Event=save_data->
case my_db_module:write(StateData#state.data) of
ok -> {stop, normal, ok, StateData};
_ -> {reply, database_error, statename, StateData)
end.
其中my_db_module:寫入是在實施實際的數據庫寫入非功能碼的一部分。
我看到兩個主要問題與此代碼:第一,FSM的純功能概念是由非功能性的代碼的一部分混合,這也使得FSM的單元測試是不可能的。其次,實現FSM的模塊依賴於my_db_module的特定實現。
在我看來,兩種解決方案:
實施my_db_module:write_async作爲發送異步消息在一定工藝處理數據庫,在Statename的不回覆,保存在StateData,切換到wait_for_db_answer和將數據庫管理進程的結果作爲handle_info中的消息進行等待。
statename(Event, From, StateData) when Event=save_data-> my_db_module:write_async(StateData#state.data), NewStateData=StateData#state{from=From}, {next_state,wait_for_db_answer,NewStateData} handle_info({db, Result}, wait_for_db_answer, StateData) -> case Result of ok -> gen_fsm:reply(State#state.from, ok), {stop, normal, ok, State}; _ -> gen_fsm:reply(State#state.from, database_error), {reply, database_error, statename, StateData) end.
這種實現的優點是可以從eunit模塊發送任意消息,而不需要觸及真正的數據庫。該解決方案從可能的競爭條件下受苦,如果分貝的答覆早些時候,該FSM改變狀態或其他進程發送save_data到FSM。
使用一個回調函數,在編寫初始化/ 1 StateData:
init([Callback]) -> {ok, statename, #state{callback=Callback}}. statename(Event, _From, StateData) when Event=save_data-> case StateData#state.callback(StateData#state.data) of ok -> {stop, normal, ok, StateData}; _ -> {reply, database_error, statename, StateData) end.
此解決方案不從比賽條件受到影響,但如果FSM使用很多回調果然壓倒了代碼。儘管更改爲實際函數回調使得單元測試成爲可能,但它不能解決功能代碼分離的問題。
我對所有這些解決方案並不滿意。是否有一些處理純OTP/Erlang方式的問題?可能是我低估了OTP和eunit原理的問題。要解決這個