Percursos
/
Elixir
Elixir
/
Programa
/
Comportamentos
Co

Comportamentos em Elixir

1 exercício

Sobre Comportamentos

Os behaviours permitem-nos definir, num módulo de behaviour, interfaces (conjuntos de funções e macros) que podem mais tarde ser implementadas por diferentes módulos de callback. Graças à interface partilhada, esses módulos de callback podem ser usados de forma intercambiável.

Note

Repara na grafia britânica de "behaviours".

Definir behaviours

Para definir um behaviour, temos de criar um novo módulo e especificar uma lista de funções que fazem parte da interface pretendida. Cada função tem de ser definida com o atributo de módulo @callback. A sintaxe é idêntica à de uma especificação de tipo de função (@spec). Temos de especificar o nome da função, uma lista de tipos de argumentos e todos os possíveis tipos de retorno.

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

Implementar behaviours

Para adicionar um behaviour existente ao nosso módulo (criar um módulo de callback), usamos o atributo de módulo @behaviour. O seu valor deve ser o nome do módulo de behaviour que estamos a adicionar.

Depois, temos de definir todas as funções (callbacks) exigidas por esse módulo de behaviour. Se estivermos a implementar o behaviour de outra pessoa, como os behaviours integrados do Elixir Access ou GenServer, encontramos a lista de todos os callbacks do behaviour na documentação em hexdocs.pm.

Um módulo de callback não está limitado a implementar apenas as funções que fazem parte do seu behaviour. Também é possível que um único módulo implemente vários behaviours.

Para assinalar que função vem de que behaviour, devemos usar o atributo de módulo @impl antes de cada função. O seu valor deve ser o nome do módulo de behaviour 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

Implementações predefinidas de callbacks

Ao definir um behaviour, é possível fornecer uma implementação predefinida de um callback. Esta implementação deve ser definida na expressão citada da macro __using__/1. Para que os utilizadores do módulo de behaviour possam substituir a implementação predefinida, chama a macro defoverridable/1 depois da implementação da função. Aceita uma lista de palavras-chave com nomes de funções como chaves e aridades de funções 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

Repara que definir funções dentro de __using__/1 é desaconselhado para qualquer outro fim que não seja definir implementações predefinidas de callbacks, mas podes sempre definir funções noutro módulo e importá-las na macro __using__/1.

Editar via GitHub A ligação abre numa nova janela ou separador

Aprende Comportamentos