Princípios SOLID na prática
Aplique os cinco princípios SOLID a classes reais de PHP.
Princípios SOLID na prática é uma aula grátis de PHP Academy no CoddyKit. Esta é a aula 1 de 4. Você pode ler a aula completa abaixo gratuitamente — depois pratica ao vivo no navegador com um editor de código integrado e um tutor de IA 24/7. Faz parte do caminho de aprendizado de PHP Academy, e seu progresso é sincronizado entre a web e o app CoddyKit. O curso de PHP Academy inclui 4 aulas no total.
Por que SOLID
SOLID reúne cinco princípios de projeto que mantêm o PHP orientado a objetos flexível, testável e resistente à deterioração. Eles não são regras para seguir cegamente, mas heurísticas que reduzem o acoplamento e esclarecem as responsabilidades. Nesta lição, aplicaremos cada princípio a classes PHP concretas que seriam realmente entregues.
- S Responsabilidade única
- O Aberto/fechado
- L Substituição de Liskov
- I Segregação de interfaces
- D Inversão de dependência
Responsabilidade única
Uma classe deve ter um único motivo para mudar. Uma classe que cria um relatório, o formata como HTML e o envia por e-mail tem três motivos para mudar. Separe persistência, formatação e transporte.
Abaixo, Invoice apenas modela dados; a renderização e o salvamento ficam em outro lugar.
<?php
final class Invoice {
public function __construct(
public readonly string $number,
public readonly int $cents
) {}
public function total(): float { return $this->cents / 100; }
}
final class InvoiceRenderer {
public function toText(Invoice $i): string {
return sprintf('Invoice %s: $%.2f', $i->number, $i->total());
}
}
$i = new Invoice('INV-1', 12500);
echo (new InvoiceRenderer())->toText($i), PHP_EOL;
SRP: identificando o problema
A violação clássica do SRP é uma classe cujo nome contém e ou um Manager que faz tudo. Observe os métodos que mudam por motivos de negócio não relacionados. Se uma alteração na regra tributária e uma alteração na diagramação do PDF afetarem a mesma classe, ela terá responsabilidades demais.
Princípio aberto/fechado
As entidades de software devem ser abertas para extensão e fechadas para modificação. Adicionar um novo tipo de Discount não deve obrigar você a editar um switch enorme. Use polimorfismo: cada regra deve ser sua própria classe, implementando uma interface compartilhada.
<?php
interface Discount { public function apply(float $total): float; }
final class PercentOff implements Discount {
public function __construct(private float $pct) {}
public function apply(float $t): float { return $t * (1 - $this->pct / 100); }
}
final class FlatOff implements Discount {
public function __construct(private float $amount) {}
public function apply(float $t): float { return max(0, $t - $this->amount); }
}
function checkout(float $total, Discount ...$discounts): float {
foreach ($discounts as $d) { $total = $d->apply($total); }
return $total;
}
echo checkout(100, new PercentOff(10), new FlatOff(5)), PHP_EOL; // 85
Substituição de Liskov
Os subtipos devem poder ser usados em qualquer lugar onde o tipo base seja esperado, sem surpreender quem faz a chamada. O exemplo famoso é: Square extends Rectangle viola o LSP porque definir a largura não deve alterar a altura silenciosamente. Prefira modelá-los como tipos separados, por trás de uma interface comum Shape.
<?php
interface Shape { public function area(): float; }
final class Rectangle implements Shape {
public function __construct(private float $w, private float $h) {}
public function area(): float { return $this->w * $this->h; }
}
final class Square implements Shape {
public function __construct(private float $side) {}
public function area(): float { return $this->side ** 2; }
}
$shapes = [new Rectangle(2, 3), new Square(4)];
foreach ($shapes as $s) { echo $s->area(), PHP_EOL; }
LSP e contratos
O LSP também impõe limites aos contratos dos métodos. Um subtipo pode enfraquecer pré-condições e fortalecer pós-condições, nunca o contrário. Lançar um novo tipo de exceção que o tipo base nunca declarou ou retornar null quando o tipo base garante um objeto viola a substituibilidade. Os tipos de retorno covariantes e os tipos de parâmetros contravariantes do PHP impõem parte disso no nível dos tipos.
Segregação de interfaces
Os consumidores não devem depender de métodos que não usam. Uma interface Worker muito ampla, com work() e eat(), obriga um RobotWorker a criar um esboço vazio para eat(). Separe interfaces focadas em funções, para que cada implementação se comprometa apenas com o que realmente faz.
<?php
interface Workable { public function work(): string; }
interface Feedable { public function eat(): string; }
final class Human implements Workable, Feedable {
public function work(): string { return 'coding'; }
public function eat(): string { return 'lunch'; }
}
final class Robot implements Workable {
public function work(): string { return 'welding'; }
}
foreach ([new Human(), new Robot()] as $w) { echo $w->work(), PHP_EOL; }
Inversão de dependência
A política de alto nível deve depender de abstrações, não de detalhes concretos. Injete uma interface, não uma classe definida diretamente no código. Assim, você pode trocar um Mailer real por um dublê nos testes e por outro fornecedor em produção sem alterar o consumidor.
<?php
interface Mailer { public function send(string $to, string $body): void; }
final class SmtpMailer implements Mailer {
public function send(string $to, string $body): void {
echo "SMTP -> $to: $body" . PHP_EOL;
}
}
final class SignupService {
public function __construct(private Mailer $mailer) {}
public function register(string $email): void {
$this->mailer->send($email, 'Welcome!');
}
}
(new SignupService(new SmtpMailer()))->register('a@b.com');
DIP e o contêiner
Inversão de dependência é o princípio; injeção de dependência é uma técnica para aplicá-lo. Um contêiner DI (PSR-11) conecta o SmtpMailer concreto à abstração Mailer na raiz de composição. O consumidor nunca escreve new SmtpMailer(), portanto a direção da dependência no código-fonte aponta para a abstração, não para o detalhe.
<?php
// Composition root wiring (pseudo-container)
$bindings = [
Mailer::class => fn() => new SmtpMailer(),
];
$resolve = fn(string $id) => $bindings[$id]();
$service = new SignupService($resolve(Mailer::class));
$service->register('user@example.com');
SOLID em conjunto
Os princípios reforçam uns aos outros. O ISP mantém as interfaces pequenas, para que as injeções do DIP permaneçam focadas; o OCP depende de um polimorfismo que o LSP mantém seguro; o SRP fornece as classes pequenas que tornam tudo isso possível. Busque alta coesão interna e baixo acoplamento externo.
- Não crie abstrações excessivas para uma classe usada uma única vez.
- Introduza uma interface quando surgir uma segunda implementação ou um dublê de teste.
Pragmatismo
SOLID é um meio, não um fim. Uma interface prematura com uma única implementação adiciona indireção sem trazer benefícios, um caso de “generalização especulativa”. Aplique os princípios quando a mudança realmente surgir ou estiver claramente iminente. Refatorar em direção a SOLID é barato quando você já tem testes; refatorar cegamente nessa direção desde o primeiro dia é desperdício.
Verificação rápida
Qual princípio o problema de herança entre Square e Rectangle viola?
Recapitulação
Você aplicou os cinco princípios SOLID ao PHP real:
- SRP: um motivo para mudar por classe.
- OCP: estenda usando novas classes polimórficas, não editando um switch.
- LSP: os subtipos respeitam o contrato do tipo base.
- ISP: interfaces pequenas e específicas de função em vez de interfaces amplas.
- DIP: dependa de abstrações e injete os detalhes na raiz de composição.
Use-os para orientar a refatoração, não como mera formalidade.
Perguntas Frequentes
A aula “Princípios SOLID na prática” é grátis?
Sim — o texto completo de “Princípios SOLID na prática” é grátis para ler aqui na web. Para praticá-la interativamente (um editor de código integrado e um tutor de IA 24/7) e desbloquear o restante do curso de PHP Academy, atualize para CoddyKit PRO. O curso de PHP Academy inclui 4 aulas no total.
O que vou aprender em “Princípios SOLID na prática”?
Aplique os cinco princípios SOLID a classes reais de PHP. Você pratica PHP Academy com código prático que executa diretamente no navegador, e um tutor de IA 24/7 responde suas dúvidas enquanto trabalha na aula.
Preciso ter experiência prévia para começar PHP Academy?
Nenhuma experiência prévia é necessária. PHP Academy no CoddyKit é estruturado para alunos iniciantes até avançados, então você pode começar aqui ou desde o início e aprender no seu ritmo. Esta é a aula 1 de 4.
Quanto tempo leva a aula “Princípios SOLID na prática”?
A maioria das aulas CoddyKit leva cerca de 5–10 minutos. Cada uma é compacta e interativa, então você faz progresso constante e retoma exatamente de onde parou entre web e app.
Posso escrever e executar código nesta aula de PHP Academy?
Sim. Cada aula de PHP Academy inclui um editor de código integrado, então você escreve e executa código real direto no navegador e recebe feedback de IA instantaneamente — nenhuma configuração local necessária.
Todas as aulas deste curso
- Princípios SOLID na prática
- Padrões de criação: Factory, Builder e Singleton
- Padrões estruturais: Adapter, Decorator e Facade
- Padrões comportamentais: Strategy, Observer e Command