Agentモジュールは、プロセスと_receive-send_ループを生成するための抽象化を提供します。ここからは、Agentモジュールを使って開始したプロセスを「エージェントプロセス」と呼ぶことにします。_エージェントプロセス_は、中心となる共有状態を表すために選ばれることがあります。
_エージェントプロセス_を開始するには、Agentが提供するstart/2関数を使います。startするときに渡した関数が、その_エージェントプロセス_の初期状態を返します。
# Start an agent process with an initial value of an empty list
{:ok, agent_pid} = Agent.start(fn -> [] end)
MapやListと同じように、Agentにも_エージェントプロセス_を扱うための関数がたくさん用意されています。
モデル化しようとしているドメインについて、Agent関連の機能をすべて1つのモジュールにまとめてカプセル化するのが通例です。
地域コミュニティの協会から、コミュニティガーデンの登録を管理するためのシンプルな登録アプリケーションを実装してほしいと頼まれました。Plot構造体はすでに用意されています。
CommunityGarden.start/1関数を実装します。この関数は、エージェントプロセスに引き渡すオプションのキーワードリストを任意で受け取ります。庭の初期状態は、区画の空のコレクションを表すように初期化します。庭のpidを含む:okタプルを返します。
{:ok, pid} = CommunityGarden.start()
# => {:ok, #PID<0.112.0>}
CommunityGarden.list_registrations/1関数を実装します。この関数は、コミュニティガーデンのpidを受け取ります。登録済みの、保存されている区画のリストを返します。
CommunityGarden.list_registrations(pid)
# => []
この時点では、区画を登録する機能はまだ追加していないので、このリストは空になるはずです
CommunityGarden.register/2関数を実装します。この関数は、コミュニティガーデンのpidと、区画を登録する相手の名前を受け取ります。成功すると、区画のidと登録先の人が入ったPlot構造体を返します。
idは連番で一意である必要があります。エージェントの状態にidカウンターを保持しておくとよいでしょう。
CommunityGarden.register(pid, "Emma Balan")
# => %Plot{plot_id: 1, registered_to: "Emma Balan"}
CommunityGarden.list_registrations(pid)
# => [%Plot{plot_id: 1, registered_to: "Emma Balan"}]
CommunityGarden.release/2関数を実装します。この関数は、解放する区画のpidとidを受け取ります。成功すると:okを返します。区画を解放してもidは再利用されず、一意の識別子としてのみ使われます。
CommunityGarden.release(pid, 1)
# => :ok
CommunityGarden.list_registrations(pid)
# => []
CommunityGarden.get_registration/2関数を実装します。この関数は、確認する区画のpidとidを受け取ります。区画が登録済みならその区画を返し、未登録なら:not_foundを返します。
CommunityGarden.get_registration(pid, 1)
# => %Plot{plot_id: 1, registered_to: "Emma Balan"}
CommunityGarden.get_registration(pid, 7)
# => {:not_found, "plot is unregistered"}