رفتارها به ما امکان میدهند که رابطها (مجموعههایی از توابع و ماکروها) را در یک ماژول رفتار تعریف کنیم که بعداً میتوانند توسط ماژولهای کالبک مختلف پیادهسازی شوند. به لطف رابط مشترک، این ماژولهای کالبک را میتوان بهجای یکدیگر به کار برد.
به املای بریتانیایی «behaviours» توجه کنید.
برای تعریف یک رفتار، باید یک ماژول جدید بسازیم و فهرستی از توابعی را مشخص کنیم که بخشی از رابط موردنظر هستند. هر تابع باید با استفاده از ویژگی ماژول @callback تعریف شود. نحوهی نگارش آن دقیقاً مثل مشخصات نوع تابع (@spec) است. باید اسم تابع، فهرستی از انواع آرگومانها و همهی انواع بازگشتی ممکن را مشخص کنیم.
defmodule Countable do
@callback count(collection :: any) :: pos_integer
end
برای افزودن یک رفتار موجود به ماژولمان (ساختن یک ماژول کالبک) از ویژگی ماژول @behaviour استفاده میکنیم. مقدار آن باید اسم ماژول رفتاری باشد که داریم اضافه میکنیم.
سپس باید همهی توابعی (کالبکهایی) را تعریف کنیم که آن ماژول رفتار به آنها نیاز دارد. اگر داریم رفتار شخص دیگری را پیادهسازی میکنیم، مثل رفتارهای توکار Access یا GenServer در Elixir، فهرست همهی کالبکهای آن رفتار را در مستندات hexdocs.pm پیدا میکنیم.
یک ماژول کالبک محدود به پیادهسازی فقط توابعی که بخشی از رفتارش هستند نیست. امکان دارد یک ماژول بهتنهایی چندین رفتار را پیادهسازی کند.
برای مشخص کردن اینکه هر تابع از کدام رفتار میآید، باید قبل از هر تابع از ویژگی ماژول @impl استفاده کنیم. مقدار آن باید اسم ماژول رفتاری باشد که این کالبک را تعریف میکند.
defmodule BookCollection do
@behaviour Countable
defstruct [:list, :owner]
@impl Countable
def count(collection) do
Enum.count(collection.list)
end
def mark_as_read(collection, book) do
# other function unrelated to the Countable behaviour
end
end
هنگام تعریف یک رفتار، میتوان پیادهسازی پیشفرضی برای یک کالبک فراهم کرد. این پیادهسازی باید در عبارت نقلقولشدهی ماکروی __using__/1 تعریف شود. برای اینکه کاربران ماژول رفتار بتوانند پیادهسازی پیشفرض را بازنویسی کنند، بعد از پیادهسازی تابع، ماکروی defoverridable/1 را فراخوانی کنید. این ماکرو یک لیست کلیدواژه میپذیرد که کلیدهایش اسم توابع و مقادیرش تعداد آرگومانهای آنها هستند.
defmodule Countable do
@callback count(collection :: any) :: pos_integer
defmacro __using__(_) do
quote do
@behaviour Countable
def count(collection), do: Enum.count(collection)
defoverridable count: 1
end
end
end
توجه کنید که تعریف توابع درون __using__/1 برای هر هدفی جز تعریف پیادهسازیهای پیشفرض کالبک توصیه نمیشود، اما همیشه میتوانید توابع را در ماژول دیگری تعریف کنید و آنها را در ماکروی __using__/1 ایمپورت کنید.