Trilhas
/
Elixir
Elixir
/
Programa
/
Behaviours
Be

Behaviours em Elixir

1 exercício

Sobre Behaviours

Behaviours permitem definir interfaces (conjuntos de funções e macros) em um módulo de behaviour que depois podem ser implementados por diferentes módulos de callback. Graças à interface compartilhada, esses módulos de callback podem ser usados de forma intercambiável.

Note

Repare na grafia britânica de "behaviours".

Definindo behaviours

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

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

Implementando behaviours

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

Depois, precisamos definir todas as funções (callbacks) exigidas por esse módulo de behaviour. Se estivermos implementando o behaviour de outra pessoa, como os behaviours Access ou GenServer que já vêm no Elixir, encontraríamos a lista de todos os callbacks desse behaviour na documentação em hexdocs.pm.

Um módulo de callback não se limita 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 marcar qual função vem de qual behaviour, devemos usar o atributo de módulo @impl antes de cada função. Seu valor deve ser o nome do módulo de behaviour que define esse 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 padrão de callbacks

Ao definir um behaviour, é possível fornecer uma implementação padrão de um callback. Essa implementação deve ser definida na quoted expression da macro __using__/1. Para que seja possível para quem usa o módulo de behaviour substituir a implementação padrão, chame a macro defoverridable/1 depois da implementação da função. Ela aceita uma lista de palavras-chave com nomes de funções como chaves e aridades de função 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

Repare que definir funções dentro de __using__/1 é desaconselhado para qualquer finalidade que não seja definir implementações padrão de callbacks, mas você sempre pode definir funções em outro módulo e importá-las na macro __using__/1.

Editar via GitHub O link abre em uma nova janela ou aba

Aprenda Behaviours