Dicas de Design Orientado a Objetos com Ruby - Parte 3

Publicado por Luiz Cezer Marrone Filho no dia dev

Desenvolvimento de Software

Neste terceiro e último post da série de dicas de Design Orientado a Objetos com Ruby, fecharei a série com algumas dicas relacionadas a como melhorar a comunicação entre suas classes deixando mais claras suas interfaces públicas.

Se você ainda não leu na parte 1 expliquei como tornar as classes mais coesas e na parte 2 como torná-las menos acopladas.

Tipos de modificadores em Ruby

Ruby, assim como outras linguagens, possui alguns tipos de modificadores de acesso para seus atributos e métodos. Esses modificadores servem para alterar a visibilidade dos atributos ou métodos e determinar quais atributos e métodos de uma classes podem ser acessados em outros pontos do software e quais podem ser acessados somente internamente a classe.

Os tipos de modificadores em Ruby são:

  • public: Por padrão todos os métodos são públicos e podem ser acessados em qualquer parte do código.
  • private: Métodos ou atributos que estão sob esse modificador são apenas visíveis no escopo da classe e não podem ser acessados em outros pontos do software. Além disso não é possível executar esses métodos com um receiver explícito.
  • protected: Assim como o private não permite acesso a variáveis e métodos fora do escopo da classe, porém permite executar os métodos através de um receiver explícito.

Se você quiser saber mais sobre visibilidade de métodos e atributos no Ruby, veja nesse artigo e também nesse outro artigo, pois esses assuntos estão fora do escopo desse post.

Métodos públicos

É muito importante que a leitura dos métodos seja fácil e clara. Dessa forma, diminui-se a chance de erros e aumenta a velocidade do programador que for usar as funções públicas.

Em linhas gerais, métodos públicos podem ter algumas características, como:

  • Revelar qual é a responsabilidade primária de um método, porém não têm a necessidade de revelar como ele faz isso.
  • É esperado que sejam executados por outras classes.
  • Devem ser estáveis (evitar que sejam alterados com frequência).
  • Devem ser seguros para quem depende deles (não é ideal que sua assinatura mude com frequência).
  • Devem ser testados e, onde necessário, documentados.
  • Não necessariamente devem lidar com detalhes de implementação.

Métodos privados

Para simplificar o código presente dentro de uma interface pública e garantir que determinados métodos dentro de uma classe sejam apenas acessíveis internamente a própria classe, é possível fazer o uso de métodos ou atributos privados.

Métodos privados devem ter a responsabilidade de lidar com os detalhes da implementação e garantir o escopo que determinadas ações só seram acessíveis na própria classe.

De modo geral, métodos privados:

  • Devem lidar com detalhes da implementação.
  • Não devem ser invocados por outras classes que não sejam a classe de seu próprio escopo.

Lei de Demeter

A Lei de Demeter ou Princípio do Menor Conhecimento, é uma lei que visa detectar trechos de código que indicam alto acoplamento entre classes ou objetos. Em outra palavras, o que essa lei prega é “Converse apenas com seu amigo imediato” ou então “Não converse com estranhos”.

De acordo com essa lei, Um método m de uma instância de um objeto o só poderá interagir com métodos de:

  • Do próprio objeto ao qual seu escopo pertence.
  • Um argumento próprio.
  • Um objeto criado dentro de si.
  • Qualquer propriedade/atributo do objeto ao qual seu escopo pertence.

Imaginando o seguinte cenário de uma aplicação Rails onde essa lei é quebrada, pode-se ter uma classe Customer que possui um método para retonar o nome do estado ao qual esse customer pertence:

1
2
3
4
5
class Customer
  def state
    address.state.name
  end
end

Esse método viola a Lei de Demeter pelo fato de que para saber qual o nome do estado do cliente, é preciso accessar o objeto address e ainda um objeto state, deixando o código muito acoplado nesse ponto e também mais suscetível a erros, já que alterações na classe Address ou State podem refletir em erros dentro da classe Customer.

Uma das formas de melhorar essa abordagem e minimizar problemas pelo alto acoplamento é seguir a risca a Lei de Demeter e não permitir que métodos precisem conhecer muitos objetos para ter seu retorno esperado, outra alternativa é através do uso de Delegators, como expliquei na segunda parte dessa série.

Tell Don’t ask! - Diga o que fazer e não peça como fazer!

Tell Don't Ask é uma prática na orientação a objetos que reforça o conceito de encapsulamento.

De modo simples essa prática prega que devemos dizer a um objeto o que ele deve fazer e não pedir a um objeto qual seu estado atual, para então tomarmos alguma ação.

Um exemplo que viola o Princípio de Tell Don’t ask:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
class SocialMediaPost
  def send
    SocialMediaPost::API.post(args)
  end

  def connected?
    connected
  end
end

post = SocialMediaPost.new(content: 'Some Message')

if post.connected?
  post.send
end

Obedecendo o Princípio é preciso “inverter” a lógica e ao invés de pedir um estado do objeto para assim executar sua ação, devemos executar a ação e internamente o próprio objeto deve saber se executa ou não a determinada ação.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
class SocialMediaPost
  def send
    SocialMediaPost::API.post(args) if connected?
  end

  private

  def connected?
    connected
  end
end

post = SocialMediaPost.new(content: 'Some Message')
post.send

Dessa forma não é preciso expor detalhes internos do objeto para serem verificados antes de cada ação, é possível centralizar qualquer futura validação e também faz com que o objeto mantenha seu encapsulamento de modo correto.

Considerações Finais

  • Interfaces públicas devem ser estáveis e expor somente o que o método faz, sem expor ou se preocupar com o como ele faz.
  • É importante pensar bem na hora de nomear um método público de forma que fique claro seu objetivo.
  • Lei de Demeter pode ajudar a identificar quando classes estão muito acopladas.
  • Tell Don’t Ask ajuda a manter o encapsulamento das classes e evitar que o código acabe ficando muito procedual.

E você concorda já conhecia algumas dessas práticas ? Gostou da série de posts sobre design orientado a objetos ?

Compartilhe nos comentários.

Luiz Cezer Marrone Filho

Luiz Cezer Marrone Filho

Full Stack Developer

Comentários