Betrachten Sie, ich habe eine FSM mit gen_fsm implementiert. Für ein Event in einem StateName sollte ich Daten in die Datenbank schreiben und dem User das Ergebnis antworten. So ist der folgende Statusname durch eine Funktion dargestellt wird:Grundlagen von OTP. Wie trennt man funktionalen und nicht-funktionalen Code in der Praxis?
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.
wo my_db_module: Schreib ist ein Teil des nicht-funktionalen Code tatsächliche Datenbank-Write-Implementierung.
Ich sehe zwei große Probleme mit diesem Code: der erste, ein reines funktionales Konzept von FSM ist durch einen Teil des nicht-funktionalen Codes gemischt, dies macht auch Unit-Tests von FSM unmöglich. Zweitens hängt ein Modul, das eine FSM implementiert, von einer bestimmten Implementierung von my_db_module ab.
Meiner Meinung nach, sind zwei Lösungen möglich:
Implement my_db_module: write_async als eine asynchrone Nachricht zu einem gewissen Prozessabwicklung Datenbank zu senden, antworten Sie nicht in state, speichern Von in StateData, wechseln Sie in wait_for_db_answer und Warten Sie auf das Ergebnis des Datenbankverwaltungsprozesses als Nachricht in einer handle_info.
Vorteile einer solchen Implementierung ist die Möglichkeit, beliebige Nachrichten von eunit-Modulen zu senden, ohne die eigentliche Datenbank zu berühren. Die Lösung leidet an möglichen Wettlaufbedingungen, wenn db früher antwortet, dass FSM den Status ändert oder ein anderer Prozess save_data an FSM sendet.
eine Rückruffunktion verwenden, geschrieben während init/1 in 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.
Diese Lösung leidet nicht unter Rennbedingungen, aber wenn FSM es viele Rückrufe verwendet überwältigt wirklich den Code. Obwohl der Wechsel zum tatsächlichen Funktionsrückruf das Komponententest ermöglicht, löst er das Problem der funktionalen Codetrennung nicht.
Ich bin nicht mit all diesen Lösungen zufrieden. Gibt es ein Rezept, um dieses Problem auf eine reine OTP/Erlang Weise zu behandeln? Vielleicht ist es mein Problem, Prinzipien von OTP und Eunit zu untermauern.