Behaviours em Elixir

Assim como Protocols, Behaviours também são uma forma de manter um padrão de interface/herança e até mesmo polimorfismo no Elixir.

Vale lembrar que Elixir é uma linguagem de programação funcional, portanto, não é o nosso forte o conceito de objeto, cl…


This content originally appeared on DEV Community and was authored by Willian Frantz

Assim como Protocols, Behaviours também são uma forma de manter um padrão de interface/herança e até mesmo polimorfismo no Elixir.

Vale lembrar que Elixir é uma linguagem de programação funcional, portanto, não é o nosso forte o conceito de objeto, classe e métodos. No final, sempre estaremos falando sobre módulos e funções!

Então de que me serve possuir uma abstração para criar interfaces em uma tecnologia como esta?

Cenário

Imaginem que vamos desenvolver uma aplicação onde a pessoa insere seus dados para consultar todos seus cartões de crédito, verificar se as faturas estão em dia, e inclusive gerar possíveis estratégias financeiras, etc...

Para isso precisamos criar uma API REST que irá consultar de diversas fontes de cartões para validar os dados e processar as respostas finais.

Você consegue identificar um padrão nisso? Se precisamos consultar mais de uma API externa para reunir os dados para a resposta final, isso significa que nosso código terá muitos módulos que serão correspondentes a provedores diferentes (ex: Itaú, Nubank, etc...)

Problema

Como iremos mapear todos esses módulos de provedores que vamos criar dentro do nosso sistema então? Como garantir que eles precisam ter uma execução semelhante, retornando os dados processados da mesma forma?

Tendo em vista que as APIs externas podem ser distintas, possuir dados diferentes e respostas diferentes, isso pode se tornar um problema e pode nos levar a criar códigos aleatórios para solucionar cada tipo de API de uma forma desnecessária.

Behaviours

Com behaviours conseguimos criar um padrão comportamental, definindo uma assinatura pré-exigida pelo módulo, garantindo que todos os módulos subsequentes que irão herdar daquela especialidade precisarão implementar aqueles requisitos.

Em outras palavras, considere sendo essa a nossa interface de acesso à APIs externas:

defmodule Bank.API do
  @moduledoc """
  Este módulo é responsável por definir uma interface 
  padrão para os demais módulos de acesso ao 
  Banco implementarem.
  """

  @type params() :: map()
  @type response() :: {:ok, map()} | {error, String.t()}

  @doc """
  Este método irá acessar a base externa e
  retornar os dados bancários da pessoa usuária
  """
  @callback call(params()) :: response()
end

Ignorando os moduledocs e typespecs do nosso módulo, nos resta essa @callback, que é exatamente o comando utilizado para definir as funções que deverão ser implementadas pelo módulo que pretende se especializar nessa API.

Perceba o quão genérico nossa interface ficou, ela só da a entender que é uma API de Banco, com uma função de chamada. Em momento algum foi especificado qual o tipo de banco, ou como essa chamada será feita (via: API, gRPC, SOAP, etc...), isso será total responsabilidade de quem for implementar essa interface.

Agora com nosso behaviour bem definido, podemos seguir para a implementação do primeiro Client que iremos utilizar em nossa aplicação para consultar os dados:

defmodule Bank.Nubank do
  @moduledoc """
  Módulo que implementa a requisição para a API do Nubank.
  """

  @behaviour Bank.API

  @impl true
  def call(%{user_id: id}) do
    id
    |> nubank_url()
    |> HTTPoison.get()
    |> case do
      {:ok, %{status_code: 200, body: response}} ->
        {:ok, response}

      {:error, reason} ->
        {:error, reason}
    end
  end

  defp nubank_url(id), do:
      Application.get_env(:my_app, :nubank)[:url] <> "/user/#{id}"
end

(a requisição acima é meramente ilustrativa ?)

O comando @behaviour Bank.API descreve que nosso módulo Nubank irá se especializar no Bank.API, logo, ele deverá implementar a seguinte função: call/1.

Com isto, temos nosso primeiro provedor implementado, yaay ?!

Conclusão

Gosto de dizer que estamos definindo padrões comportamentais, com isso, quando vemos que um módulo específico implementa um Behaviour, já dá para ter uma ideia de quais funções ele roda, qual o seu propósito, como ele deve ser implementado em termos de input e output.

Esse tipo de padronização ajuda a escalar nosso código, diminuir a curva de aprendizagem para novas pessoas desenvolvedoras e inclusive é uma ótima ferramenta para auto-documentar nosso código, deixa tudo mais descritivo e explícito!

E você, o que acha de behaviours? Gostaria de complementar, adicionar ou remover alguma informações deste tópico?

that's all


This content originally appeared on DEV Community and was authored by Willian Frantz


Print Share Comment Cite Upload Translate Updates
APA

Willian Frantz | Sciencx (2021-06-01T01:42:39+00:00) Behaviours em Elixir. Retrieved from https://www.scien.cx/2021/06/01/behaviours-em-elixir/

MLA
" » Behaviours em Elixir." Willian Frantz | Sciencx - Tuesday June 1, 2021, https://www.scien.cx/2021/06/01/behaviours-em-elixir/
HARVARD
Willian Frantz | Sciencx Tuesday June 1, 2021 » Behaviours em Elixir., viewed ,<https://www.scien.cx/2021/06/01/behaviours-em-elixir/>
VANCOUVER
Willian Frantz | Sciencx - » Behaviours em Elixir. [Internet]. [Accessed ]. Available from: https://www.scien.cx/2021/06/01/behaviours-em-elixir/
CHICAGO
" » Behaviours em Elixir." Willian Frantz | Sciencx - Accessed . https://www.scien.cx/2021/06/01/behaviours-em-elixir/
IEEE
" » Behaviours em Elixir." Willian Frantz | Sciencx [Online]. Available: https://www.scien.cx/2021/06/01/behaviours-em-elixir/. [Accessed: ]
rf:citation
» Behaviours em Elixir | Willian Frantz | Sciencx | https://www.scien.cx/2021/06/01/behaviours-em-elixir/ |

Please log in to upload a file.




There are no updates yet.
Click the Upload button above to add an update.

You must be logged in to translate posts. Please log in or register.