私はgen_fsmで実装されたFSMを持っているとします。いくつかのStateNameのいくつかのイベントに対して、私はデータベースにデータを書き込んで、結果をユーザーに返すべきです。したがって、次のステート名が関数で表される。OTPの原則。実際に機能コードと非機能コードを分離する方法は?
my_db_modulestatename(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.
:書き込みが実際のデータベース書き込みを実現非機能コードの一部です。
このコードには2つの大きな問題があります。まず、FSMの純粋な機能概念が非機能コードの一部で混在しているため、FSMの単体テストも不可能になります。第2に、FSMを実装するモジュールは、my_db_moduleの特定の実装に依存します。私の意見で
、2つの解決策が考えられます
はmy_db_module実装します、ステート名に返信StateData中から保存、wait_for_db_answerに切り替わりません、データベースを扱ういくつかのプロセスに非同期メッセージを送信するようwrite_asyncをし、 handle_info内のメッセージとしてdb管理プロセスの結果を待ちます。
このような実装の利点は、実際のデータベースに触れずにeunitモジュールから任意のメッセージを送信できることです。この解決策は、dbが先に応答すると、FSMが状態を変更したり、別のプロセスがsave_dataをFSMに送信したりする可能性がある競合状態に悩まされます。このソリューションは、競合状態に苦しんでいませんが、FSMは、多くのコールバックを使用している場合、それは本当にコードを圧倒
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.
:
はStateData内のinit/1の間に書かれたコールバック関数を使用します。実際の関数コールバックに変更することで単体テストが可能になりますが、関数コードの分離の問題は解決しません。
このソリューションはすべて快適ではありません。純粋なOTP/Erlangの方法でこの問題を処理する方法がいくつかありますか? OTPとeunitの原則を過小評価することが私の問題です。