Tracks
/
Elixir
Elixir
/
Temario
/
Comportamientos
Co

Comportamientos en Elixir

1 ejercicio

Acerca de Comportamientos

Los comportamientos nos permiten definir interfaces (conjuntos de funciones y macros) en un módulo de comportamiento que luego pueden ser implementadas por diferentes módulos de callback. Gracias a la interfaz compartida, esos módulos de callback se pueden usar de forma intercambiable.

Note

Fíjate en la ortografía británica de «behaviours».

Definir comportamientos

Para definir un comportamiento, necesitamos crear un nuevo módulo y especificar una lista de funciones que forman parte de la interfaz deseada. Cada función debe definirse usando el atributo de módulo @callback. La sintaxis es idéntica a una especificación de tipo de función (@spec). Necesitamos especificar un nombre de función, una lista de tipos de argumentos y todos los tipos de retorno posibles.

defmodule Countable do
  @callback count(collection :: any) :: pos_integer
end

Implementar comportamientos

Para añadir un comportamiento existente a nuestro módulo (crear un módulo de callback) usamos el atributo de módulo @behaviour. Su valor debe ser el nombre del módulo de comportamiento que estamos añadiendo.

Luego, necesitamos definir todas las funciones (callbacks) que requiere ese módulo de comportamiento. Si estamos implementando el comportamiento de otra persona, como los comportamientos integrados de Elixir Access o GenServer, encontraríamos la lista de todos los callbacks del comportamiento en la documentación en hexdocs.pm.

Un módulo de callback no se limita a implementar solo las funciones que forman parte de su comportamiento. También es posible que un único módulo implemente varios comportamientos.

Para marcar qué función proviene de qué comportamiento, debemos usar el atributo de módulo @impl antes de cada función. Su valor debe ser el nombre del módulo de comportamiento que define este callback.

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

Implementaciones de callback predeterminadas

Al definir un comportamiento, es posible proporcionar una implementación predeterminada de un callback. Esta implementación debe definirse en la expresión citada de la macro __using__/1. Para que los usuarios del módulo de comportamiento puedan sobrescribir la implementación predeterminada, llama a la macro defoverridable/1 después de la implementación de la función. Acepta una lista de palabras clave con nombres de funciones como claves y aridades de funciones como valores.

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

Ten en cuenta que definir funciones dentro de __using__/1 no es recomendable para ningún otro propósito que no sea definir implementaciones de callback predeterminadas, pero siempre puedes definir funciones en otro módulo e importarlas en la macro __using__/1.

Editar en GitHub El enlace se abre en una nueva ventana o pestaña

Aprende Comportamientos