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

  1. A classe tem mais de uma responsabilidade? (SRP)
  2. Para adicionar funcionalidade, preciso modificar código existente? (OCP)
  3. Subclasses podem substituir classes base sem quebrar o sistema? (LSP)
  4. Interfaces são específicas ou genéricas demais? (ISP)
  5. 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