Principios SOLID en PHP: teoría y práctica
SOLID son cinco principios fundamentales de la programación orientada a objetos, formulados por Robert Martin. Ayudan a crear código flexible, mantenible y escalable. En este artículo analizaremos cada principio con ejemplos en PHP, desde conceptos simples hasta escenarios prácticos reales.
1. Single Responsibility Principle (SRP) — Principio de responsabilidad única
Formulación: Una clase debe tener solo una razón para cambiar. En otras palabras, cada clase es responsable de una sola tarea o funcionalidad.
La violación de SRP a menudo conduce a "objetos divinos" que saben y hacen demasiado. Veamos un mal ejemplo:
class Report { public function generate($data) { // Generación del reporte return "Reporte: " . json_encode($data); }
public function saveToFile($filename, $content) { file_put_contents($filename, $content); }
public function sendEmail($to, $subject, $body) { mail($to, $subject, $body); }}Aquí la clase Report es responsable de generar, guardar y enviar — tres responsabilidades diferentes. Corrijamos esto:
class ReportGenerator { public function generate($data) { return "Reporte: " . json_encode($data); }}
class FileSaver { public function save($filename, $content) { file_put_contents($filename, $content); }}
class EmailSender { public function send($to, $subject, $body) { mail($to, $subject, $body); }}
// Uso$report = new ReportGenerator();$content = $report->generate($data);
$saver = new FileSaver();$saver->save('report.txt', $content);
$sender = new EmailSender();$sender->send('admin@example.com', 'Reporte listo', $content);Consejo práctico: Si ves en el nombre de una clase la conjunción "y" (por ejemplo, UserAndLogger), es una señal clara de violación de SRP.
2. Open-Closed Principle (OCP) — Principio de abierto/cerrado
Formulación: Las clases deben estar abiertas para extensión, pero cerradas para modificación. Debes poder agregar nueva funcionalidad sin cambiar el código existente.
La violación de OCP ocurre cuando agregar un nuevo tipo requiere modificar clases existentes. Ejemplo de mal código:
class PaymentProcessor { public function process($type, $amount) { if ($type === 'credit_card') { // Procesamiento de tarjeta de crédito } elseif ($type === 'paypal') { // Procesamiento de PayPal } elseif ($type === 'crypto') { // Procesamiento de criptomoneda } // Agregar un nuevo tipo = modificar esta clase }}La solución es usar polimorfismo a través de interfaces:
interface PaymentMethod { public function process($amount);}
class CreditCardPayment implements PaymentMethod { public function process($amount) { echo "Procesando tarjeta de crédito por $amount"; }}
class PayPalPayment implements PaymentMethod { public function process($amount) { echo "Procesando PayPal por $amount"; }}
class PaymentProcessor { public function process(PaymentMethod $method, $amount) { $method->process($amount); }}
// Uso$processor = new PaymentProcessor();$processor->process(new CreditCardPayment(), 100);$processor->process(new PayPalPayment(), 200);Ahora agregar un nuevo método de pago (por ejemplo, ApplePayPayment) no requiere modificar PaymentProcessor.
3. Liskov Substitution Principle (LSP) — Principio de sustitución de Liskov
Formulación: Los objetos de las subclases deben ser intercambiables con los objetos de la clase base