SOLID na prática: exemplos reais em PHP e TypeScript
1. Introdução ao SOLID no contexto de Temas — Lista Final (1200 temas)
O SOLID é um tema fundamental na Lista Final de 1200 temas porque representa a base da programação orientada a objetos de qualidade. Ele se conecta diretamente com temas vizinhos como nomenclatura (nomes expressivos facilitam a aplicação dos princípios), padrões de projeto (muitos padrões implementam SOLID) e code review (os princípios servem como checklist de verificação).
Os cinco princípios — SRP, OCP, LSP, ISP e DIP — são igualmente aplicáveis em PHP e TypeScript, embora as diferenças de tipagem influenciem a implementação. Vamos explorar cada um com exemplos práticos do mundo real.
2. Single Responsibility Principle (SRP) — Uma classe, um motivo para mudar
Exemplo em PHP: e-commerce
Antes (violação SRP):
class Pedido {
public function calcularTotal() { /* regras de negócio */ }
public function salvarNoBanco() { /* persistência */ }
public function enviarEmailConfirmacao() { /* notificação */ }
}
Depois (SRP aplicado):
class Pedido {
public function calcularTotal(): float { /* apenas regras de negócio */ }
}
class PedidoRepository {
public function salvar(Pedido $pedido): void { /* apenas persistência */ }
}
class NotificadorPedido {
public function enviarConfirmacao(Pedido $pedido): void { /* apenas notificação */ }
}
Exemplo em TypeScript: serviço de notificações
Antes:
class NotificacaoService {
enviarEmail(mensagem: string): void { /* lógica de email */ }
enviarSMS(mensagem: string): void { /* lógica de SMS */ }
registrarLog(mensagem: string): void { /* logging */ }
}
Depois:
class EmailService {
enviar(mensagem: string): void { /* apenas email */ }
}
class SMSService {
enviar(mensagem: string): void { /* apenas SMS */ }
}
class LoggerService {
registrar(mensagem: string): void { /* apenas logging */ }
}
Benefício prático: Cada classe tem um único motivo para mudar. Testes unitários ficam isolados e code reviews focam em responsabilidades específicas.
3. Open/Closed Principle (OCP) — Aberto para extensão, fechado para modificação
Implementação em PHP com Strategy Pattern
interface EstrategiaDesconto {
public function calcular(float $valor): float;
}
class DescontoFixo implements EstrategiaDesconto {
public function calcular(float $valor): float {
return $valor - 10;
}
}
class DescontoPercentual implements EstrategiaDesconto {
private float $percentual;
public function __construct(float $percentual) {
$this->percentual = $percentual;
}
public function calcular(float $valor): float {
return $valor * (1 - $this->percentual / 100);
}
}
class CalculadoraPreco {
public function calcular(float $valor, EstrategiaDesconto $estrategia): float {
return $estrategia->calcular($valor);
}
}
Implementação em TypeScript com interfaces
interface CalculadoraImposto {
calcular(valor: number): number;
}
class ICMS implements CalculadoraImposto {
calcular(valor: number): number {
return valor * 0.18;
}
}
class ISS implements CalculadoraImposto {
calcular(valor: number): number {
return valor * 0.05;
}
}
class CalculadoraNotaFiscal {
constructor(private calculadora: CalculadoraImposto) {}
gerar(valor: number): number {
return valor + this.calculadora.calcular(valor);
}
}
Diferença chave: TypeScript exige contratos explícitos (interfaces), enquanto PHP permite duck typing, mas ambos suportam polimorfismo.
4. Liskov Substitution Principle (LSP) — Subtipos devem ser substituíveis
Exemplo problemático em PHP
class Retangulo {
protected int $largura;
protected int $altura;
public function setLargura(int $valor): void { $this->largura = $valor; }
public function setAltura(int $valor): void { $this->altura = $valor; }
public function getArea(): int { return $this->largura * $this->altura; }
}
class Quadrado extends Retangulo {
public function setLargura(int $valor): void {
$this->largura = $valor;
$this->altura = $valor; // violação: altera comportamento inesperado
}
public function setAltura(int $valor): void {
$this->largura = $valor;
$this->altura = $valor; // violação: quebra a substituibilidade
}
}
Correção em TypeScript com composição
interface FormaGeometrica {
getArea(): number;
}
class Retangulo implements FormaGeometrica {
constructor(private largura: number, private altura: number) {}
getArea(): number {
return this.largura * this.altura;
}
}
class Quadrado implements FormaGeometrica {
constructor(private lado: number) {}
getArea(): number {
return this.lado * this.lado;
}
}
Detecção em code review: Pergunte: "Se eu substituir a classe base pela derivada, o comportamento esperado se mantém?"
5. Interface Segregation Principle (ISP) — Muitas interfaces específicas
Exemplo em PHP
Antes (interface inchada):
interface Worker {
public function trabalhar(): void;
public function comer(): void;
public function dormir(): void;
}
Depois (interfaces segregadas):
interface Workable {
public function trabalhar(): void;
}
interface Eatable {
public function comer(): void;
}
interface Sleepable {
public function dormir(): void;
}
class Humano implements Workable, Eatable, Sleepable {
public function trabalhar(): void { /* implementação */ }
public function comer(): void { /* implementação */ }
public function dormir(): void { /* implementação */ }
}
class Robo implements Workable {
public function trabalhar(): void { /* implementação */ }
// Não precisa implementar comer() nem dormir()
}
Exemplo em TypeScript: processamento de pagamentos
interface ProcessadorPagamento {
processar(valor: number): boolean;
}
interface ValidadorPagamento {
validar(dados: any): boolean;
}
interface NotificadorPagamento {
notificar(cliente: string, status: string): void;
}
class PagamentoPix implements ProcessadorPagamento, ValidadorPagamento {
processar(valor: number): boolean { /* lógica Pix */ }
validar(dados: any): boolean { /* validação específica */ }
}
Benefício: Classes implementam apenas o que precisam, reduzindo acoplamento e facilitando manutenção.
6. Dependency Inversion Principle (DIP) — Dependa de abstrações
Implementação em PHP com injeção de dependência
interface LoggerInterface {
public function log(string $mensagem): void;
}
class LoggerArquivo implements LoggerInterface {
public function log(string $mensagem): void {
file_put_contents('app.log', $mensagem, FILE_APPEND);
}
}
class LoggerBanco implements LoggerInterface {
public function log(string $mensagem): void {
// salvar no banco de dados
}
}
class ProcessadorPedido {
private LoggerInterface $logger;
public function __construct(LoggerInterface $logger) {
$this->logger = $logger;
}
public function processar(Pedido $pedido): void {
// lógica de processamento
$this->logger->log("Pedido processado: " . $pedido->getId());
}
}
Implementação em TypeScript com inversão de controle
interface RepositorioUsuario {
buscarPorId(id: string): Promise<Usuario | null>;
salvar(usuario: Usuario): Promise<void>;
}
class RepositorioUsuarioMySQL implements RepositorioUsuario {
async buscarPorId(id: string): Promise<Usuario | null> {
// consulta MySQL
}
async salvar(usuario: Usuario): Promise<void> {
// INSERT MySQL
}
}
class ServicoUsuario {
constructor(private repositorio: RepositorioUsuario) {}
async atualizarEmail(id: string, novoEmail: string): Promise<void> {
const usuario = await this.repositorio.buscarPorId(id);
if (!usuario) throw new Error('Usuário não encontrado');
usuario.email = novoEmail;
await this.repositorio.salvar(usuario);
}
}
Relação com temas vizinhos: O DIP se alinha com "Convention over configuration" (convenções de nomes para injeção automática) e com o padrão Factory (criação de dependências).
7. Conclusão e integração com a Lista Final
Os cinco princípios SOLID se reforçam mutuamente:
- SRP + ISP: classes pequenas com interfaces específicas
- OCP + DIP: extensibilidade através de abstrações
- LSP: garante que o polimorfismo funcione corretamente
Checklist prático para code review
- A classe tem mais de uma responsabilidade? (SRP)
- Para adicionar funcionalidade, preciso modificar código existente? (OCP)
- Subclasses podem substituir classes base sem quebrar o sistema? (LSP)
- Interfaces são específicas ou genéricas demais? (ISP)
- Dependências são de abstrações ou implementações concretas? (DIP)
Próximos passos
SOLID é a porta de entrada para padrões de projeto como Factory (criação desacoplada), Singleton (controle de instância) e Observer (notificações). Combine com boas práticas de nomenclatura para criar código autodocumentado e de fácil manutenção.
Referências
- Princípios SOLID — Documentação oficial da Microsoft — Explicação dos cinco princípios com exemplos em C# (aplicáveis a qualquer linguagem OO)
- SOLID: Os 5 princípios da POO — Alura — Artigo completo em português com exemplos práticos em PHP
- TypeScript Handbook: Interfaces — Documentação oficial sobre interfaces, base para aplicar ISP e DIP em TypeScript
- Refactoring Guru: SOLID Principles — Explicação visual dos princípios com exemplos em múltiplas linguagens
- PHP The Right Way: Design Patterns — Guia de boas práticas PHP, incluindo aplicação de SOLID com padrões de projeto
- Clean Code: A Handbook of Agile Software Craftsmanship — Robert C. Martin, criador do acrônimo SOLID, com exemplos profundos de aplicação