Open/Closed Principle - PHP

Abierto para la extensión y cerrado para la modificación es lo que nos dice este principio.

Rodríguez Patiño, Eduardo
2020-11-27 | 400 lecturas

Para crear un código altamente escalable la idea sería que no tengamos que modificar una clase, sino más bien extenderla.

Ejemplo

Supongamos que tenemos una clase para mandar distintidas notificaciones ya sea por sms, e-mail, entre otros y en un futuro se podría incluir slack, whatsapp, etc.

class NotificationService
{
    public function send(Array $notifications) : send
    {
        foreach($notifications as $notification) 
        {
            if ($notification->type === "sms") 
            {
                $this->endbySMS($notification->phoneNumber, $notification->subject);
            }

            if ($notification->type === "email")
            {
                $this->sendbyEmail($notification->email, $notification->subject);
            }
        }
    }

    private function sendbySMS(string $phoneNumber, string $subject) 
    {
        // Logica para mandar el SMS
    }

    private function sendbyEmail(string $to, string $subject)
    {
        // Logica para mandar el email
    }
}

El problema

Radica en que el método principal de nuestra clase la vamos a estar modificando constante en función a la cantidad de proveedores de notificación que tengamos.

Solución

Una forma bastante simple y elegante de resolver esto es a través de una interfaz aplicando polimorfismo. Es decir, que nuestro método de disparar las notificaciones recibirá un array de dicha de interfaz.

interface INotification 
{
    function send() : void;
}

Ahora vamos a separar las lógicas de los envío de notificaciones a clases independientes y que implementen nuestra interfaz.

class NotificationEmailService implements INotification
{
    private string $to;
    private string $subject;

    public function __construct(string $to, string $subject)
    {
        $this->to = $to;
        $this->subject = $subject;
    }

    public function send() : void
    {
        // Lógica para enviar la ntoification por e-mail
    }
}

class NotificationSMSService implements INotification
{
    private string $phoneNumber;
    private string $subject;

    public function __construct(string $phoneNumber, string $subject)
    {
        $this->phoneNumber = $to;
        $this->subject = $subject;
    }

    public function send() : void
    {
        // Lógica para enviar la ntoification por e-mail
    }
}

Nuestro nueva clase para disparar todas las notificaciones quedaría de la siguiente manera.

class NotificationService
{
    public function send(Array $notifications) : void
    {
        foreach($notifications as $notification) 
        {
            $notification->send();
        }
    }
}

¿Bastante lighweight no?

¿Cómo quedaría su uso?

$notifications = [
    new NotificationEmailService("customer@email.com", "El motivo del correo"),
    new NotificationSmsService("+05199999", "El asunto del mensaje de texto")
];

$notificationService = new NotificationService();
$notificationService->send($notifications);

Conclusión

Al implementar la misma interfaz hacemos uso de polimorfismo para trabajar de manera abstracta. Es decir, no me importa quien es el proveedor de notificación, solo me interesa lo que debe hacer.