Esse site utiliza cookies
Nós armazenamos dados temporariamente para melhorar a sua experiência de navegação e recomendar conteúdo do seu interesse.
Ao utilizar os nossos serviços, você concorda com as nossas políticas de privacidade.
Esse site utiliza cookies
Nós armazenamos dados temporariamente para melhorar a sua experiência de navegação e recomendar conteúdo do seu interesse.
Ao utilizar os nossos serviços, você concorda com as nossas políticas de privacidade.
Categoria de Programação
Postado em 14 abril 2022
Atualizado em 02 junho 2023
Palavras-chave: srp,ocp,single,responsibility,principle,clean,architecture,classe,programacao
Visualizações: 1945
Com o surgimento de linguagens orientadas a objetos, programadores passaram a ter um novo grande problema: dependências desnecessária entre classes. A dependência entre classes em si, não é a origem do problema. O verdadeiro problema é como o código é organizado. É muito comum encontrarmos uma classe com várias funções úteis com o nome “Utilidades” em alguns sistemas. Porém, essa prática não é recomendável, porque muitas classes vão começar a usar essas funções e depender da classe “Utilidades”. Quando uma classe tem muitas dependências, ela acaba se tornando estável (difícil de modificar), pois existem muitas classes dependendo dessa classe. O princípio da responsabilidade única (Single Responsibility Principle) surge como uma solução para esse problema.
Esse princípio foi criado por Robert Martin e ganhou relevância após a publicação da arquitetura limpa. O princípio de responsabilidade única foi publicado pela primeira vez em 2003 em um artigo chamado “Os princípios de OOD” (The Principles of OOD) por Robert Martin.
Esse princípio aborda que uma classe só deve ter uma razão para mudar. Mas pelo fato de uma classe só ter uma razão para mudar, isso não significa que ela deve ter apenas uma função. Além disso, é bastante comum desenvolvedores chegarem a conclusão que esse princípio fala que uma classe só pode fazer uma coisa. Porém, o próprio Robert diz que isso não é verdade. No livro de 2018 sobre a arquitetura limpa, Robert trouxe uma nova descrição sobre esse princípio:
Um módulo deve ser responsável por apenas um ator
O módulo se refere ao código-fonte (source code). E o ator se refere a classe que usa esse módulo. Em outras palavras, o que Robert quer dizer com esse princípio está por trás das dependências entre classes.
Veja na imagem acima um exemplo de violação do princípio de responsabilidade única. O exemplo acima é uma violação de SRP porque dois atores diferentes estão compartilhando o mesmo módulo. O módulo “Enviar mensagem” possui apenas uma função: enviar mensagem.
Compartilhar uma mesma função entre atores diferentes pode resultar em uma duplicação acidental. A duplicação acidental pode acontecer quando a função “Enviar mensagem” muda em favor de apenas um dos atores. O grande problema aqui é que o modo de um humano e um alienígena de mandar mensagens é diferente. Logo, uma modificação no módulo acaba fazendo com que um dos atores dependentes da função “Enviar mensagem” quebre ou tenha comportamentos estranhos.
Quando esse princípio diz que um módulo só deve ter uma responsabilidade, ele também se refere as dependências do módulo. Se olharmos apenas a classe de modo isolado e esquecermos das dependências, dificilmente saberemos por quantos motivos ela realmente pode mudar. O SRP diz que um módulo só deve fazer uma coisa, fazer bem e fazer apenas isso. Em resumo, devemos olhar dentro (o que uma classe faz) e fora (dependências entre classes) de um módulo.
Uma solução para o exemplo do humano e do alienígena seria separar o módulo em dois.
Separando os módulos por ator resolve o problema de duplicação acidental e traz mais algumas vantagens:
A diminuição do acoplamento entre classes se refere as dependências que usam o mesmo módulo. Se um módulo tiver 2 dependências, ele terá duas razões para mudar, se um módulo tiver 3 dependências ele terá 3 razões para mudar. Conforme o número de atores aumenta, o acoplamento também aumenta. Esse tipo de acoplamento torna o código frágil e robusto, pois esse módulo deve atender a 3 tipos de atores diferentes. Se tivermos apenas um único ator usando esse módulo, teremos muito mais facilidade para modificar o conteúdo.
O princípio de segregação de interfaces diz que uma classe não deve depender de coisas que ela não usa. Se vários atores compartilham um mesmo módulo, mais chances esse módulo tem de abrigar conteúdo desnecessário para determinados atores. Se dividirmos cada módulo por ator, evitamos esse a importação de conteúdo desnecessário.
A estabilidade se refere a robustez de uma classe. Quanto mais dependências, mais motivos essa classe tem para não mudar. É bastante comum ver classes voláteis (classes que mudam com frequência) sendo usadas por diversos atores em um sistema. Isso pode ser perigoso, pois quando uma classe muda ela tem grandes chances de influenciar as dependências.
Se há uma desvantagem nesse princípio, essa desvantagem é ter que localizar cada classe para utilização. Uma consequência de usar o SRP é que as classes ficam segregadas e bem menos extensas. Robert diz que para resolver essa problema poderíamos usar Facades. Facades são classes especializadas em instanciar classes.
Assim, o ator “Humano” pode localizar e instanciar todas as classes que ela precisa apenas usando a classe “HumanFacade”.
Mesmo se os atores tem o mesmo comportamento, não é recomendado compartilhar o mesmo módulo, porque esses atores podem mudar por motivos diferentes. Esse princípio serve como uma proteção antecipada para que atores não tenham o seu comportamento influenciado pela modificação de outro ator.
Como vimos no exemplo acima, apenas olhando para o conteúdo dentro de um módulo é difícil saber todos os motivos para ele mudar.
class MessageSender {
Protocol protocol;
String ipAddress;
int portNo;
public boolean sendMessage(String message) {
if (!isValidAddress) {
throw new Exception("Endereco IP inválido");
}
return protocol.sendMessage(message);
}
private boolean isValidAddress () {
return protocol.check(ipAddress + ":" + portNo);
}
}
Avaliando pelo nome da classe, temos a impressão que essa classe é compartilhada entre atores. Na maioria das vezes essa impressão é precisa.
MessageSender messageSender = new MessageSender();
...
Alien alien = new Alien(messageSender);
alien.sendMessage("Hello World");
Human human = new Human(messageSender);
human.sendMessage("Hello Mars");
Dois atores diferentes compartilham a mesma classe “MessageSender” quando instanciados, violando o princípio de responsabilidade única. Imagine que o ator alienígena precisa fazer uma mudança no modo que ele manda a mensagem, sendo obrigado a mudar o conteúdo “MessageSender”. Essa modificação teria grandes chances de afetar o comportamento do humano, que também depende dessa classe. Se dividirmos cada módulo por ator, eliminamos esse tipo de preocupação.
Alien alien = new Alien(new AlienMessageSender());
alien.sendMessage("Hello World");
Human human = new Human(new HumanMessageSender());
human.sendMessage("Hello Mars");
Esse é apenas um exemplo de solução para que estejamos de acordo com o SRP. Os princípios SOLID nos mostram outras abordagens para esse tipo de problema, como o princípio aberto fechado. Ao aplicarmos o princípio fechado, podemos mudar o comportamento de um módulo estendendo ele. Isso é possível graças as abstrações.
O princípio de responsabilidade única diz que um módulo só deve fazer uma coisa e bem feita. Porém, é difícil dizer quantas coisas um módulo faz olhando apenas o conteúdo dentro dele. É necessário conferirmos os atores que dependem desse módulo para dizer quantos motivos ele tem para mudar.
Projetos práticos
Detectando objetos que entram dentro do campo de visão do personagem. Útil para servir de "gatilho" para eventos em um jogo.
Simulação dos gráficos do segundo turno das eleições presidenciais, utilizando python e ferramentas de análise de dados, pandas e jupyter.
Implementando um programa que encontra a menor distância entre dois pontos dentro de um labirinto usando o algoritmo A* (a-estrela).
Programando o clássico jogo da serpente usando o framework p5.js. Tutorial indicado para iniciantes da programação que querem aprender os conceitos básico da área criando jogos.
Fazendo a integração contínua de Jenkins, Sonatype Nexus, Sonatype, JUnit e Gradle para automatizar processos repetitivos. Prática bastante usada em tecnologias de DevOps.
Hoje em dia não é mais necessário gastar o nosso tempo com tarefas que podem se automatizadas. Os robôs estão aqui para nos ajudar...
Muitos serviços já utilizam a autenticação biométrica integrada com a inteligência artificial para melhorar a experiência do usuário, além de melhorar a segurança.
Pilha e fila são tipos de estrutura de dados que contribuem para um gerenciamento de dados mais inteligente e eficaz na programação
A notação do O grande é um método de fácil implementação, usado para avaliar a eficiência de um algoritmo em relação ao tempo de processamento.
Ao ser aplicado permite que os detalhes passem a depender de abstrações, respeitando a direção da regra de dependências.
CSR é a sigla para responsabilidade social corporativa. Essa expressão descreve práticas que contribuem para o bem da sociedade.