Patrones de Diseño

Adapter es uno de los patrones de diseño más simples. Un adaptador es una clase que provee un enlace entre dos clases o interfaces incompatibles entre sí para que puedan trabajar en conjunto.

Los ejemplos más sencillos de Adaptador los tenemos en el mundo real: queremos cargar nuestro teléfono conectándolo a la corriente principal pero solo tenemos un cable USB ¿Qué hacemos? Conectamos el cable USB a un Adaptador y luego el Adaptador a la toma de corriente.

En este ejemplo el cable USB es la interfaz adaptada y la toma de corriente es la interfaz objetivo.

Adaptador

Un caso parecido sucede si estamos viajando de España a Reino Unido, puesto que las tomas de corriente son diferentes necesitamos un Adaptador para conectar nuestros equipos electrónicos.

Ya que estás familiarizado con el concepto veamos cómo aplicarlo a la programación:

Imagina que estamos trabajando con un nuevo servicio que nos permite subir videos a la nube a través de drones llamado DroneBox.

La interfaz para trabajar con DroneBox es un poco peculiar subimos archivos con el método liftoff($fileName) y los obtenemos con el método land($fileName)

<?php

class DroneBox
{
    public function liftoff($file, $content)
    {
        // Upload a file to the cloud
    }

    public function land($file)
    {
        // Download a file from the cloud
    }
}

Por otro lado, el framework que utilizamos en el proyecto no soporta DroneBox entre sus servicios de sistema de archivos, aunque nos permite agregar cualquier sistema de archivos que soporte la siguiente interfaz:

<?php

namespace Component;

interface FilesystemInterface
{
    /**
     * Create a file or update if exists.
     *
     * @param string $path    The path to the file.
     * @param string $content The file content.
     *
     * @return bool True on success, false on failure.
     */
    public function put($path, $content);

    /**
     * Read a file.
     *
     * @param string $path The path to the file.
     *
     * @throws FileNotFoundException
     *
     * @return string|false The file contents or false on failure.
     */
    public function read($path);
}

Antes de darnos por vencidos y renunciar a nuestro sistema de archivos guiado por drones se nos ocurre la siguiente idea:

¿Qué tal si creamos una clase intermedia que nos permita adaptar la peculiar interfaz de DroneBox a la interfaz que necesita el Framework o componente? Por ejemplo:

<?php

namespace App;

class DroneBoxAdapter
{

}

Nuestra nueva clase DroneBoxAdapter debe implementar la interfaz FilesystemInterface que pertenece al componente de terceros:

<?php

namespace App;

use Component\FilesystemInterface;

class DroneBoxAdapter implements FilesystemInterface
{
    public function put($path, $content)
    {

    }

    public function read($path)
    {
        
    }
}

A continuación vamos a recibir una instancia de la clase DroneBox a través del constructor de DroneBoxAdapter y a almacenarla como una propiedad dentro del adaptador:

<?php

class DroneBoxAdapter implements FilesystemInterface
{
    /**
     * @var DroneBox
     */
    private $droneBox;

    function __construct(DroneBox $droneBox)
    {
        $this->droneBox = $droneBox;
    }

    //...
}

Nota cómo el Adaptador «envuelve» la clase que va a ser «adaptada». De hecho el Patrón Adaptador también se conoce como Wrapper.

Ahora cada vez que se llamen a los métodos put y read del Adaptador vamos a delegar a los métodos originales de la clase adaptada, de esta forma:

<?php

//...

class DroneBoxAdapter implements FilesystemInterface
{
    //...

    public function put($path, $content)
    {
        return $this->droneBox->liftoff($path, $content);
    }

    public function read($path)
    {
        return $this->droneBox->land($path);
    }
}

Gracias a esto, cada vez que necesitemos pasar un objeto de la clase FilesystemInterface a nuestro componente de archivos y queramos usar DroneBox solamente tendremos que envolverlo en el nuevo adaptador que creamos:

<?php

//...

// Clase que requiere la interfaz FilesystemInterface
class FileManager
{
    public function setDriver(string $name, FilesystemInterface $interface);
}

// Ejemplo de uso:
$fileManager->setDriver('dronebox', new DroneBoxAdapter(new DroneBox));

Puesto que el Adaptador implementa la interfaz FilesystemInterface ahora podemos usar la clase DroneBox sin problema.

Un punto interesante a notar es que la clase objetivo no sabe que está interactuando con un Adaptador o intermediario puesto que este luce como cualquier otra clase que implemente la interfaz requerida.

Si estás interesado en continuar aprendiendo más sobre programación orientada a objetos y patrones de diseño no debes perderte estos cursos los cuales incluyen ejemplos más complejos y explicaciones detalladas de las características de OOP que incluye PHP:

 

Regístrate hoy en Styde y obtén acceso a todo nuestro contenido.