行動連鎖

みなさんこんにちは! 最初のメモでは、一連の動作を作成することについてかなり言及しました。 この中で、説明付きの単純なチェーンの例を挙げたいと思います。



私は、コードに関する批判やコメントを喜んで受け取ります。



それでは、実装の詳細に立ち入ることなく、キーと値のペアを保存するための普遍的なインターフェースが目標であることを想像してみましょう。 最初の段階でやりたいことは、「動作する」部分のインターフェースを決定することです。



-callback list_items() -> [term()]. -callback add_item(Id :: term(), Value :: any() )->ok|{error, any() }. -callback get_item(Id :: term()) -> {ok, Value::any()}|{error, not_found}. -callback del_item(Id :: term()) -> ok|{error, not_found}.
      
      





インターフェイスは非常にシンプルで、値の保存場所(ツリー、ハッシュテーブル、または単純なリスト)を決定しません。



次の瞬間-どの実装を決定しようとしても、それは間違いなく内部状態を持つことになります-同じツリー、テーブル、またはリストです。 そして、それを保存するために、標準のgen_serverを使用します。 つまり、gen_serverを実装するモジュールによって提供される状態に基づいてインターフェースを実装するコードが作成されます。 ここにある。



今初期化の瞬間。 実装モジュールの開始時に、代わりにインターフェイスモジュールを開始する必要があり、次に、彼はgen_serverを使用します。



このようなもの:



実装:



 start_link(_Params)-> storage:start_link(example_storage, []).
      
      





インターフェース:



 start_link(Module, ModArgs) -> gen_server:start_link({local, ?SERVER}, ?MODULE, {Module, ModArgs}, []).
      
      





呼び出しチェーンの次のステップは、インターフェイスモジュールのinit関数です。 次に、実装モジュールで対応する関数を呼び出すといいでしょう。 さらに、これらすべての情報を保存する必要があります。 合計:



インターフェース:



 -record(state, {mod :: atom(), mod_state :: term() }). init({Mod, ModArgs}) -> process_flag(trap_exit, true), {ok, ModState} = Mod:init_storage(ModArgs), {ok, #state{mod = Mod, mod_state = ModState}}.
      
      





実装:



 init_storage([])-> Table = [], {ok, [{table, Table }]}.
      
      





はい、はい! この実装は、単純なリストにデータを保存します。 ホラー



初期化がすべてです。 作業部に戻りましょう。 データはインターフェイスステータス内に保存されます。 それを呼び出して処理を依頼しましょう:



実装:



 -spec list_items() -> [term()]. list_items()-> storage:do_action(list_items, {}).
      
      





インターフェース:



 -spec do_action(atom(), tuple())-> term(). do_action(Command, Command_params)-> gen_server:call(?MODULE, {Command, Command_params}). handle_call({Command, Command_params}, _From, State = #state{mod = Mod, mod_state = ModState}) -> {Reply, UpdatedState} = Mod:execute_action(ModState, Command, Command_params), {reply, Reply, State#state{mod_state = UpdatedState}}.
      
      





呼び出しを取得し、内部状態を取得して、要求を処理する関数を呼び出します。



実装:



 -spec execute_action(State::term(), Action::atom(), ActionParams :: tuple() ) -> {term(), term()}. execute_action(State = [{table, Table }], list_items, {}) -> {Table, State}.
      
      





それでは、すべてをまとめましょう。



インターフェース:



 -module(storage). -behaviour(gen_server). -callback start_link(Params :: [term()])-> {ok, pid() }. -callback init_storage(Args :: list())-> {ok, State :: term()}. -callback list_items() -> [term()]. -callback add_item(Id :: term(), Value :: any() )->ok|{error, any() }. -callback get_item(Id :: term()) -> {ok, Value::any()}|{error, not_found}. -callback del_item(Id :: term()) -> ok|{error, not_found}. -callback execute_action(State::term(), Action::atom(), ActionParams :: tuple() ) -> {term(), term()}. -export([start_link/2]). -export([init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2, code_change/3]). -export([do_action/2]). -define(SERVER, ?MODULE). -record(state, {mod :: atom(), mod_state :: term() }). start_link(Module, ModArgs) -> gen_server:start_link({local, ?SERVER}, ?MODULE, {Module, ModArgs}, []). -spec do_action(atom(), tuple())-> term(). do_action(Command, Command_params)-> gen_server:call(?MODULE, {Command, Command_params}). init({Mod, ModArgs}) -> process_flag(trap_exit, true), {ok, ModState} = Mod:init_storage(ModArgs), {ok, #state{mod = Mod, mod_state = ModState}}. handle_call({Command, Command_params}, _From, State = #state{mod = Mod, mod_state = ModState}) -> {Reply, UpdatedState} = Mod:execute_action(ModState, Command, Command_params), {reply, Reply, State#state{mod_state = UpdatedState}}. handle_cast(_Msg, State) -> {noreply, State}. handle_info(_Info, State) -> {noreply, State}. terminate(_Reason, _State) -> ok. code_change(_OldVsn, State, _Extra) -> {ok, State}.
      
      





そして実装:



 -module(example_storage). -behaviour(storage). -export([start_link/1, init_storage/1, list_items/0, add_item/2, get_item/1, del_item/1, execute_action/3]). -spec start_link(Params :: list())-> {ok, pid() }. start_link(_Params)-> storage:start_link(example_storage, []). -spec init_storage(Args :: list())-> {ok, State :: term()}. init_storage([])-> Table = [], {ok, [{table, Table }]}. -spec list_items() -> [term()]. list_items()-> storage:do_action(list_items, {}). -spec add_item(Id :: term(), Value :: any() )->ok|{error, any() }. add_item(Id, Value)-> storage:do_action(add_item, {Id, Value}). -spec get_item(Id :: term()) -> {ok, Value::any()}|{error, not_found}. get_item(Id)-> storage:do_action(get_item, {Id}). -spec del_item(Id :: term()) -> ok|{error, not_found}. del_item(Id)-> storage:do_action(del_item, {Id}). -spec execute_action(State::term(), Action::atom(), ActionParams :: tuple() ) -> {term(), term()}. execute_action(State = [{table, Table }], list_items, {}) -> {Table, State}; execute_action(_State = [{table, Table }], add_item, {Id, Value}) -> UpdatedTable = lists:keystore(Id, 1, Table, {Id, Value}), {ok, [{table, UpdatedTable }]}; execute_action(State = [{table, Table }], get_item, {Id}) -> case lists:keyfind(Id, 1, Table) of false -> { {error, not_found}, State}; {Id, Value} -> {Value, State} end; execute_action(State = [{table, Table }], del_item, {Id}) -> case lists:keymember(Id, 1, Table) of true -> UpdatedTable = lists:keydelete(Id, 1, Table), {ok, [{table, UpdatedTable }] }; false -> { {error, not_found}, State} end. -ifdef(TEST). -include_lib("eunit/include/eunit.hrl"). simple_test()-> {ok, Pid} = example_storage:start_link([]), [] = example_storage:list_items(), ok = example_storage:add_item(key1, 2), [{key1,2}] = example_storage:list_items(), ok = example_storage:add_item(key2, 4), [{key1,2}, {key2, 4}] = example_storage:list_items(), ok = example_storage:del_item(key1), [{key2, 4}] = example_storage:list_items(), {error, not_found} = example_storage:del_item(key1), {error, not_found} = example_storage:get_item(key1), 4 = example_storage:get_item(key2), ok = example_storage:del_item(key2), [] = example_storage:list_items(). -endif.
      
      





これはシンプルで非常に単純な実装です。 実際には、インターフェイスモジュールには、さまざまなソリューションと補助機能に共通のコードが含まれています。 一般化されたアプローチにより、コードの柔軟性が高まり、テストが簡素化されます。



All Articles