Principios SOLID en la práctica
Aplique los cinco principios SOLID a clases PHP reales.
Principios SOLID en la práctica es una lección gratuita de PHP Academy en CoddyKit. Esta es la lección 1 de 4. Puedes leer la lección completa abajo gratuitamente — luego la practicas en el navegador con un editor de código integrado y un tutor de IA 24/7. Forma parte de la ruta de aprendizaje de PHP Academy, y tu progreso se sincroniza en la web y la app de CoddyKit. El curso de PHP Academy incluye 4 lecciones en total.
Por qué SOLID
SOLID consta de cinco principios de diseño que mantienen el código PHP orientado a objetos flexible, comprobable y resistente al deterioro. No son reglas que deban seguirse ciegamente, sino heurísticas que reducen el acoplamiento y aclaran las responsabilidades. En esta lección aplicaremos cada principio a clases PHP concretas que realmente implementaría.
- SResponsabilidad única
- OAbierto/cerrado
- LSustitución de Liskov
- ISegregación de interfaces
- DInversión de dependencias
Responsabilidad única
Una clase debería tener un único motivo para cambiar. Una clase que genera un informe, le da formato HTML y lo envía por correo tiene tres motivos para cambiar. Separe la persistencia, el formato y el transporte.
A continuación, Invoice solo modela los datos; la representación y el guardado se realizan en otros lugares.
<?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: detectar el olor
La infracción clásica del SRP es una clase cuyo nombre contiene and o un Manager que se encarga de todo. Preste atención a los métodos que cambian por motivos de negocio no relacionados. Si un cambio en las reglas fiscales y un cambio en el diseño del PDF afectan a la misma clase, esta tiene demasiadas responsabilidades.
Principio abierto/cerrado
Las entidades de software deberían estar abiertas a la extensión y cerradas a la modificación. Añadir un nuevo tipo de descuento no debería obligarle a editar un switch enorme. Utilice polimorfismo: cada regla debe ser su propia clase que implemente una interfaz común.
<?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
Sustitución de Liskov
Los subtipos deben poder utilizarse en cualquier lugar donde se espere su tipo base, sin sorprender al código que los invoca. El ejemplo clásico: Square extends Rectangle infringe el LSP porque establecer el ancho no debe cambiar silenciosamente la altura. Es preferible modelarlos como tipos independientes detrás de una interfaz común 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 y los contratos
El LSP también limita los contratos de los métodos. Un subtipo puede debilitar las precondiciones y reforzar las poscondiciones, pero nunca al contrario. Lanzar un nuevo tipo de excepción que el tipo base nunca declaró, o devolver null cuando el tipo base garantiza un objeto, infringe la sustituibilidad. Los tipos de retorno covariantes y los tipos de parámetros contravariantes de PHP aplican parte de esto en el nivel de tipos.
Segregación de interfaces
Los clientes no deberían depender de métodos que no utilizan. Una interfaz Worker demasiado extensa, con work() y eat(), obliga a RobotWorker a crear un stub para eat(). Divida la interfaz en interfaces de roles específicos para que cada implementación solo se comprometa con lo que realmente hace.
<?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; }
Inversión de dependencias
La política de alto nivel debería depender de abstracciones, no de detalles concretos. Inyecte una interfaz, no una clase codificada directamente. Así podrá sustituir un mailer real por uno simulado en las pruebas y por otro proveedor en producción sin modificar el 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 y el contenedor
La inversión de dependencias es el principio; la inyección de dependencias es una de las técnicas para cumplirlo. Un contenedor de DI (PSR-11) conecta el SmtpMailer concreto con la abstracción Mailer en la raíz de composición. El consumidor nunca escribe new SmtpMailer(), por lo que la dirección de la dependencia del código fuente apunta hacia la abstracción, no hacia el detalle.
<?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 en conjunto
Los principios se refuerzan entre sí. El ISP mantiene pequeñas las interfaces para que las inyecciones del DIP sigan centradas; el OCP depende de un polimorfismo que el LSP mantiene seguro; el SRP proporciona las clases pequeñas que hacen posible todo esto. Aspire a lograr cohesión interna y bajo acoplamiento entre clases.
- No abstraiga en exceso una clase que solo se utiliza una vez.
- Introduzca una interfaz cuando aparezca una segunda implementación o un objeto simulado para pruebas.
Pragmatismo
SOLID es un medio, no un fin. Una interfaz prematura con una sola implementación añade indirección sin aportar beneficios («generalidad especulativa»). Aplique los principios cuando el cambio llegue realmente o sea claramente inminente. Refactorizar hacia SOLID resulta económico cuando ya cuenta con pruebas; refactorizar ciegamente hacia SOLID desde el primer día es un desperdicio.
Comprobación rápida
¿Qué principio infringe el problema de herencia entre Square y Rectangle?
Repaso
Ha aplicado los cinco principios SOLID a PHP real:
- SRP: un único motivo para cambiar por clase.
- OCP: extender mediante nuevas clases polimórficas, no editando un switch.
- LSP: los subtipos respetan el contrato del tipo base.
- ISP: interfaces pequeñas y orientadas a roles en lugar de interfaces demasiado extensas.
- DIP: depender de abstracciones e inyectar los detalles en la raíz de composición.
Utilícelos para orientar la refactorización, no como un ritual.
Preguntas frecuentes
¿La lección «Principios SOLID en la práctica» es gratis?
Sí — el texto completo de «Principios SOLID en la práctica» es gratis para leer aquí en la web. Para practicarla de forma interactiva (editor de código integrado y tutor de IA 24/7) y desbloquear el resto del curso de PHP Academy, actualiza a CoddyKit PRO. El curso de PHP Academy incluye 4 lecciones en total.
¿Qué aprenderé en «Principios SOLID en la práctica»?
Aplique los cinco principios SOLID a clases PHP reales. Practicas PHP Academy con código real que ejecutas directamente en el navegador, y un tutor de IA 24/7 responde tus preguntas mientras trabajas en la lección.
¿Necesito experiencia previa para empezar PHP Academy?
No se requiere experiencia previa. PHP Academy en CoddyKit está estructurado para principiantes hasta estudiantes avanzados, así que puedes empezar aquí o desde el inicio y avanzar a tu ritmo. Esta es la lección 1 de 4.
¿Cuánto tiempo toma la lección «Principios SOLID en la práctica»?
La mayoría de las lecciones de CoddyKit toman alrededor de 5–10 minutos. Cada una es compacta e interactiva, así que avanzas constantemente y retomas exactamente por donde dejaste en la web y la app.
¿Puedo escribir y ejecutar código en esta lección de PHP Academy?
Sí. Cada lección de PHP Academy incluye un editor de código integrado, así que escribes y ejecutas código real directamente en tu navegador y obtienes retroalimentación instantánea de IA — sin configuración local necesaria.
Todas las lecciones de este curso
- Principios SOLID en la práctica
- Patrones creacionales: Factory, Builder y Singleton
- Patrones estructurales: Adapter, Decorator y Facade
- Patrones de comportamiento: Strategy, Observer y Command