[SOLID] Princípio da Responsabilidade Única com Ruby
O princípio da Responsabilidade Única (Single Responsibility Principle, SRP, em inglês) é o primeiro dos cinco princípios do SOLID. Ele fala que cada modulo de software deve ter apenas uma razão para mudar.
Você já deve ter ouvido alguém pregando que um modulo só deve fazer uma unica tarefa. Isso não está incorreto. Quando refatoramos funções, sempre precisamos quebrá-la para que ela faça o minimo de coisas possíveis. Porém esse conceito não reflete a ideia do SRP.
Podemos pensar de uma forma mais correta, que a responsabilidade não são as tarefas do mudulo, mas o modulo é responsável por alguém, que agora chamaremos de ator. Ator, pode tratar-se de um grupo de usuários, um outro modulo ou mesmo outro software que faça uso deste.
Veja esse conceito agora com mais clareza
Um módulo deve ser responsável por um, apenas um, ator. (Robert C. Martin)
Entendamos módulo como sendo as classes em Ruby.
Violando o princípio
Note que temos uma classe Payment
que tem dois métodos e cada um é responsável por um ator diferente.
Em Ruby eu teria algo semelhante a isto:
class Payment
def calculate_payment
# used by user on checkout
end
def send_to_gateway
# it uses calculate_payment and sends to PaymentGateway
end
end
Note que a classe tem dois métodos públicos e isto acende um alerta para algum problema que estejamos causando. Isso acontece porque a mesma classe está conversando com dois atores diferentes.
Mais coesão, menos acoplamento
Aqui percebemos 2 problemas, a pouca coesão e o muito acoplamento. A baixa coesão significa nossa classe está tendo mais responsabilidade do que deveria. A responsabilidade não significa necessariamente as coisas que a classe faz, mas para quem ela faz. Quanto maior o número de atores para qual a classe trabalha, maior a responsabilidade dela e menor é a coesão.
O problema de uma baixa coesão é que o nosso principio diz que ela deveria ter apenas uma razão para mudar. Então no nosso exemplo poderíamos ter o seguinte problema:
Temos 2 atores, o Checkout da plataforma, que precisa da classe Payment e o Gateway de pagamento, que precisa fazer o mesmo calculo.
Um dia a direção do negocio resolve separar as taxas de entrega, onde eram pagas no Gateway de pagamento, agora ela será paga diretamente a transportadora. Porém o Checkout deveria continuar apresentando a conta total a se pagar.
Temos o método ‘calculate_payment’, que soma o valor do produto e todas as taxas a pagar. Agora este método sofrerá mudanças. O time responsável pelo Gatway de pagamento faz a alteração da classe, removendo as taxas de entrega. Entretanto o time de Checkout não havia sido informado da mudança, talvez porque nem soubessem que eles usavam esta classe. Logo os usuários começam a reclamar que eles não sabiam que havia uma taxa separada da transportadora.
Isso acontece porque a nossa classe Payment tem muitas responsabilidade e necessita de mais de um motivo para mudar.
É natural que uma baixa coesão reflita em um alto acoplamento, que é quando uma classe tem muitas dependências de outras. Dessa forma o principio vem para nos proteger destas variáveis negativas.
Refatorando
Uma sugestão para resolver o problema é trabalhar com Facade. Assim a gente consegue por a logica de cada responsabilidade em classes separadas, mas com uma interface agrupando essas classes em um contexto.
Vejamos em Ruby como seria.
class Payment
def calculate_payment
payment_calculator= PaymentCalculator.new()
payment_calculator.call
end
def send_to_gateway
gateway_sender = GatewaySender.new()
gateway_sender.call
end
end
class PaymentCalculator
def call
# used by cliente to calculate the payment value
end
end
class GatewaySender
def call
# it uses calculate_payment and sends to PaymentGateway
end
end
Esta é uma solução simples e barata para evitar que sua classe tenha muitas responsabilidade.