Principios SOLID en PHP: aplicación avanzada y ejemplos prácticos

Entrenador de Python en Línea para Principiantes

Aprende Python fácilmente sin sobrecargarte de teoría. Resuelve tareas prácticas con verificación automática, recibe pistas y escribe código directamente en el navegador, sin instalar nada.

Start Course

Introducción: Por qué SOLID no es solo un dogma, sino una herramienta

La mayoría de los desarrolladores saben que SOLID son cinco principios de la programación orientada a objetos: Single Responsibility, Open-Closed, Liskov Substitution, Interface Segregation y Dependency Inversion. Sin embargo, en la práctica, la comprensión a menudo se reduce a seguir las reglas mecánicamente. En este artículo iremos más allá: analizaremos escenarios avanzados de aplicación de SOLID en PHP, veremos antipatrones que violan los principios y mostraremos cómo la refactorización teniendo en cuenta SOLID hace que el código sea flexible y testeable.

El material está dirigido a desarrolladores de nivel Middle+ y Senior que ya escriben en PHP y desean mejorar la arquitectura de sus proyectos.



1. Principio de responsabilidad única (SRP): cuando la clase «sabe demasiado»

La formulación clásica: «Una clase debe tener solo una razón para cambiar». A nivel avanzado, es importante entender que «razón para cambiar» es un requisito de negocio. Si tu clase cambia debido a cambios en las reglas de validación, el formato de salida y la lógica de persistencia, es una violación de SRP.



Ejemplo de violación (God Object)

class OrderProcessor {    public function process(array $data): void {        // 1. Validación de datos        if (empty($data['user_id']) || empty($data['items'])) {            throw new InvalidArgumentException('Invalid order data');        }                // 2. Cálculo del total        $total = 0;        foreach ($data['items'] as $item) {            $total += $item['price'] * $item['quantity'];        }                // 3. Guardar en BD        $sql = "INSERT INTO orders (user_id, total, status) VALUES (?, ?, 'pending')";        // ... ejecución de la consulta                // 4. Envío de email        mail($data['user_email'], 'Order confirmation', "Total: $total");    }}


Refactorización con SRP

Dividimos en tres clases con áreas de responsabilidad claras:

class OrderValidator {    public function validate(array $data): void {        if (empty($data['user_id']) || empty($data['items'])) {            throw new InvalidArgumentException('Invalid order data');        }    }}

class OrderCalculator { public function calculateTotal(array $items): float { return array_reduce($items, function ($carry, $item) { return $carry + $item['price'] * $item['quantity']; }, 0.0); }}

class OrderRepository { public function save(int $userId, float $total, string $status = 'pending'): int { // Lógica de guardado en BD return 42; // ID del pedido }}

class OrderProcessor { public function __construct( private OrderValidator $validator, private OrderCalculator $calculator, private OrderRepository $repository, private Mailer $mailer ) {} public function process(array $data): void { $this->validator->validate($data); $total = $this->calculator->calculateTotal($data['items']); $orderId = $this->repository->save($data['user_id'], $total); $this->mailer->send($data['user_email'], 'Order #' . $orderId); }}


2. Principio de abierto/cerrado (OCP): extensión sin modificación

La esencia de OCP: las clases deben estar abiertas para la extensión, pero cerradas para la modificación. En la práctica, esto significa usar polimorfismo y abstracciones en lugar de estructuras condicionales.



Violación de OCP: switch-case para cada nuevo tipo

class PaymentProcessor {    public function pay(string $type, float $amount): void {        switch ($type) {            case 'credit_card':                // procesar tarjeta                break;            case 'paypal':                // procesar PayPal                break;            // ¡Al agregar un nuevo tipo, modificamos la clase!        }    }

Blogs

Book Recommendations