تسمح لنا السلوكيات بتعريف واجهات (مجموعات من الدوال والماكرو) في وحدة سلوك يمكن أن تنفّذها لاحقًا وحدات استدعاء راجع مختلفة. وبفضل الواجهة المشتركة، يمكن استخدام وحدات الاستدعاء الراجع تلك بالتبادل.
لاحظ التهجئة البريطانية لكلمة "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.