Boas práticas de programação com a aplicação do princípio da responsabilidade única

No que diz respeito ao desenvolvimento de software, vivemos em um universo no qual transformações em sistemas são cada vez mais frequentes. Cada nova funcionalidade, até mesmo cada simples variação dela que se adapta melhor ao usuário final gera valor e, se gera valor, produz dinheiro. Assim, cada sinergia de sucesso eleva o grau de diferenciação perante concorrentes e garante melhor vantagem competitiva. Nesse contexto, a velocidade com que um produto consegue se adaptar a uma determinada necessidade é um fator crucial de sucesso. Quanto mais rápido uma necessidade é desenvolvida, melhor é para todos os envolvidos. Assim, um dos fatores que devem ser considerados para um sistema amplamente adaptável é a sua capacidade de manutenibilidade.

A capacidade de manutenibilidade é a característica que mede a facilidade com que um sistema pode ser modificado, isto é, o quão fácil se dá o desenvolvimento de melhorias, a criação de extensões ou, quando necessário, correções para ele. É uma qualidade que pode ser alcançada e que, em geral, depende da devida aplicação de abstrações como padrões de projeto e princípios de orientação a objetos bem conhecidos tais como o KISS (Keep it simple, Stupid!), DRY ( Don’t Repeat Yourself ) e, o objeto de estudo deste artigo, o princípio SOLID.

O princípio SOLID diz respeito ao acrônimo mnemônico introduzido por Robert C. Martin em meados de 2000 para definir cinco princípios que auxiliam a garantir melhor qualidade de software com melhor capacidade de manutenção. São princípios que, quando aplicados corretamente, guiam os desenvolvedores a tornar o código fonte mais extensível, legível e flexível. Das cinco regras: Single responsibility, Open-closed, Liskov substitution, Interface segregation e Dependency inversion, abordaremos neste artigo apenas o primeira delas.

Mas o que vem a ser o princípio da responsabilidade única? O Single responsibility principle (SRP) é o postulado que define que uma classe deve conhecer apenas a sua responsabilidade e ter apenas uma razão para ser modificada. Dessa maneira, é responsável por aumentar o grau coesão entre objetos tornando mais fácil a leitura e reuso de código ao passo em que mantém a complexidade em um nível gerenciável. Para um melhor entendimento, vejamos um exemplo prático de como aplicar essa regra:

Imagine que você esteja desenvolvendo um sistema para ser utilizado em uma portaria. Uma interface perfeitamente razoável que poderia representar alguma das necessidades a serem desenvolvidas seria a seguinte:

public interface Portaria {

	void cadastraVisitante();

	void autorizaEntrada();	

	void recebeCorrespondencia();	

}

Essa interface por sua vez poderia ser implementada da seguinte maneira:

public class PortariaImpl implements Portaria {

	public void cadastraVisitante() {
		// Implementação do cadastro de visitante.
	}

	public void autorizaEntrada() {
		// Implementação da rotina de autorização de entrada.
	}

	public void recebeCorrespondencia() {
		// Implementação da rotina de recepção de correspondência
	}

}

Nesse cenário, podemos observar que a classe que representa a portaria possui um grande número de responsabilidades pois nela são definidos como cadastrar um visitante e como autorizar a entrada de uma pessoa e como receber uma correspondência. O grande número de responsabilidades acarreta que, qualquer modificação pontual de uma determinada função envolve a manutenção de toda uma classe que, no melhor dos casos, terá a legibilidade comprometida dado o crescimento exponencial que ela pode tomar. Já no pior dos casos, pode vir a gerar problemas em outras funcionalides em virtude de sua baixa coesão.

Então como poderíamos melhor o grau de coesão dessa classe? Basta que criemos novos objetos que as encapsulam. Veja como:

public class GerenciadorDeCadastro {
	
	void cadastra(){
		// Aqui vai a implementação do cadastro de visitante.
	}

}
public class AutorizadorDeEntrada {

	public boolean autoriza(){
		// Aqui vai a implementação da rotina de autorização de entrada.
	}
	
}
public class ReceptorDeCorrespondencia {
	
	void recebe(){
		// Aqui a implementação da rotina de recepção de correspondência
	}

}

Em conclusão, cada responsabilidade é um ponto único de alteração o que torna mais fácil executar uma manutenção posterior além de nos guiar a um código mais interpretável. Por outro lado é importante ter em mente que, apesar de raros, existem casos que não demandam essa complexidade. Mesmo assim aplique esse princípio sempre que puder.

Referências

Código Limpo: Uma primeira abordagem por Gilmar Borba
Single Responsibility Principle por oodesign.com
SOLID – Princípio da Responsabilidade Única de Alexandre Gama
Taking the Single Responsibility Principle Seriously por Ralf Westphal

Bibliografia

MARTIN, Robert. Design Principles and Design Patterns. Disponível em: http://www.objectmentor.com/resources/articles/Principles_and_Patterns.pdf
MARTIN, Robert. Código Limpo: habilidades práticas do Agile Software. Rio de Janeiro, RJ : Alta Books, 2011.
SHORE, James; WARDEN, Shane. A Arte do Desenvolvimento Ágil. Rio de Janeiro, RJ : Alta Books, 2008.

Artigos relacionados