O que é SOLID?
SOLID é um conjunto de cinco princípios de design orientado a objetos criado por Robert C. Martin (Uncle Bob) com o objetivo de tornar o código mais fácil de manter, testar e evoluir ao longo do tempo.
S — Single Responsibility Principle (SRP)
Uma classe deve ter apenas um motivo para mudar.
O princípio da responsabilidade única determina que cada classe, módulo ou componente deve possuir apenas uma responsabilidade.
Exemplo ruim
type UserService struct{}
func (s UserService) CreateUser() {}
func (s UserService) SendEmail() {}
func (s UserService) GenerateReport() {}Neste exemplo, a mesma classe cria usuários, envia e-mails e gera relatórios.
Exemplo melhor
type UserService struct{}
type EmailService struct{}
type ReportService struct{}Cada responsabilidade fica isolada em seu próprio componente.
O — Open/Closed Principle (OCP)
Entidades devem estar abertas para extensão, mas fechadas para modificação.
O objetivo é permitir a adição de novos comportamentos sem alterar código já existente.
Exemplo ruim
func CalculateDiscount(customerType string) float64 {
if customerType == "premium" {
return 0.20
}
if customerType == "vip" {
return 0.30
}
return 0
}Sempre que surgir um novo tipo de cliente, a função precisará ser modificada.
Exemplo melhor
type Discount interface {
Value() float64
}
type Premium struct{}
func (Premium) Value() float64 { return 0.20 }
type Vip struct{}
func (Vip) Value() float64 { return 0.30 }Novos descontos podem ser criados sem alterar o código existente.
L — Liskov Substitution Principle (LSP)
Se uma classe filha herda de uma classe pai, ela deve poder substituí-la sem quebrar o sistema.
Esse princípio garante que as abstrações representem corretamente o comportamento esperado.
Exemplo ruim
type Bird interface {
Fly()
}
type Penguin struct{}
func (Penguin) Fly() {
panic("não voa")
}Um pinguim é uma ave, mas não pode voar.
Exemplo melhor
type Bird interface {
Move()
}
type FlyingBird interface {
Fly()
}As interfaces refletem melhor as capacidades reais de cada tipo.
I — Interface Segregation Principle (ISP)
Nenhum cliente deve ser obrigado a depender de métodos que não utiliza.
Interfaces devem ser pequenas e específicas.
Exemplo ruim
type Worker interface {
Work()
Eat()
Sleep()
}Um robô teria que implementar métodos sem utilidade para ele.
Exemplo melhor
type Worker interface {
Work()
}
type Eater interface {
Eat()
}
type Sleeper interface {
Sleep()
}Cada implementação utiliza apenas os contratos que realmente precisa.
D — Dependency Inversion Principle (DIP)
Dependa de abstrações, não de implementações concretas.
O objetivo é reduzir acoplamento e facilitar testes e substituições.
Exemplo ruim
type UserService struct {
db PostgresDB
}O serviço fica diretamente dependente do PostgreSQL.
Exemplo melhor
type Database interface {
SaveUser()
}
type UserService struct {
db Database
}Agora é possível utilizar PostgreSQL, MySQL, SQLite ou mocks para testes.
A visão prática do SOLID
Em projetos modernos utilizando Go, Rust, Node.js, PHP, Java ou C#, os princípios mais utilizados costumam ser:
O problema do "SOLID religioso"
Um erro comum é aplicar SOLID de forma excessiva, criando camadas e abstrações desnecessárias.
UserService
UserRepository
UserRepositoryInterface
UserRepositoryFactory
UserRepositoryProvider
UserRepositoryAdapter
UserRepositoryBuilder
UserRepositoryManagerEm uma aplicação simples, essa complexidade pode gerar mais problemas do que benefícios.
Regra prática
SOLID não existe para deixar o código bonito. Seu verdadeiro objetivo é reduzir acoplamento e tornar mudanças futuras mais baratas e seguras.