Motivação
Este é o primeiro texto de uma série onde explicarei, de maneira clara e lúdica, cada um dos conceitos do SOLID. Em vez de replicar conteúdos já disponíveis pela internet, pretendo fazer um dump das minhas experiências pessoais e do meu entendimento sobre o tema. Hoje, iniciaremos com o princípio SRP (Single Responsibility Principle).
Breve resumo
Os conceitos que formam o acrônimo SOLID já são bem conhecidos há pelo menos 20 anos e são frequentemente abordados em entrevistas técnicas. Mais do que isso, entender profundamente cada princípio melhorará significativamente a qualidade do seu código. A intenção destes textos é esclarecer definitivamente cada conceito para que você consiga aplicá-los independentemente da linguagem que utilizar.
Obs.: No final do texto, vou deixar dois repositórios com exemplos práticos implementando cada conceito em Elixir e Golang, contudo, o mais importante é compreender o fundamento para que você consiga adaptá-lo à sua realidade.
SRP - Single Responsibility Principle
Esse termo foi cunhado por Robert Martin (Uncle Bob) em 2000, no livro Design Principles and Design Patterns. Porém, esse é um problema clássico da programação, que surgiu após os anos 80 com o aumento da complexidade das aplicações. Com a complexidade crescendo, tornou-se necessário organizar melhor o código, o que impulsionou a implementação de POO no mercado, visto que já existia pelo menos há 20 anos na década de 80/90.
Segundo o livro Clean Code, o conceito de SRP pode ser resumido da seguinte maneira:
"Uma classe ou módulo deve ter um, e apenas um, motivo para mudar." (Clean Code, pág. 138)
Mas, afinal, o que isso significa? Dentro de uma estrutura, seja ela um módulo, pacote ou classe, qualquer interação que fuja da sua responsabilidade original provavelmente violará o SRP.
Veja estes dois exemplos:
- Situação 01: Dentro de uma estrutura de usuário, existem propriedades e métodos que devem sempre refletir ações e características diretamente relacionadas ao usuário. Exemplo:
type User struct {
Name string
Age int
Document string
// outras propriedades
}
// Em Go, este trecho "(u *User)" indica que esta função é um método da struct User.
func (u *User) IsAdult() bool {
// lógica aqui
}
func (u *User) ValidateDocument(document string) bool {
// lógica aqui
}
Porém, se adicionarmos algo como:
func (u *User) SaveUser() {
// lógica aqui
}
Claramente estaremos quebrando o SRP, já que salvar o usuário não deveria ser responsabilidade direta da estrutura de domínio User
.
- Situação 02: Também é possível violar esse princípio em outras camadas da aplicação. Veja o exemplo abaixo em Elixir:
# Código com violação do SRP
defmodule User do // define o módulo
def create_user(attrs) do // define a função
user =
%User{attrs}
|> Repo.insert!() // insere no banco de dados
Email.send_welcome(user) // envia email para o cliente
Logger.info("User created: \#{user.id}") // loga a ação
user // retorna o usuário
end
end
# Código seguindo o SRP
defmodule UserCreator do
def create(attrs) do
Repo.insert!(%UserStruct{attrs})
end
end
defmodule WelcomeNotifier do
def send_welcome_email(user) do
Email.send_welcome(user)
end
end
Neste exemplo em Elixir, fica claro como é fácil misturar responsabilidades. No código inicial, temos criação de usuário, envio de e-mail e log em uma única função, o que fere o SRP. O código corrigido separa claramente as responsabilidades.
Com esses exemplos, fica mais fácil entender como devemos pensar ao escrever código. Lembre-se de sempre avaliar se cada componente possui apenas uma única razão para mudar, caso haja mais de uma, com certeza o princípio SRP está quebrado nesse contexto.
Repositórios com exemplos práticos:
Se ainda houver dúvidas ou se você discordar de algo, deixe um comentário, até mais!
Top comments (0)