Patrones de Diseño en PHP

Un patrón de diseño es una estructura general que puede ser usada para solucionar un problema. No se trata ni de una librería ni de un esqueleto. Para que una solución se considere un patrón de diseño debe ser efectiva y reutilizable, es decir, que pueda aplicarse y resolver problemas similares.

Existen 3 grandes grupos de patrones de diseño: Patrones Creacionales, Patrones Estructurales y Patrones de Comportamiento.

Patrones Creacionales

Estos patrones están enfocados al proceso de creación de instancias, en particular, de ocultar el proceso de creación y de encapsular.Existen 5 tipos de patrones Creacionales:

  • - Abstract Factory
  • - Builder
  • - Factory Method
  • - Prototype
  • - Singleton

Voy a analizar los más relevantes, suficiente para poder entender este tipo de patrones:

1. Factory

Es uno de los más utilizados. En este patrón una clase intermedia (Factory) se encarga de crea el objeto que quieres utilizar.

class Coche
{
    private $marca;
    private $modelo;

    public function __construct($marca, $modelo)
    {
        $this->marca = $marca;
        $this->modelo = $modelp;
    }

    public function getMarcaYModelo()
    {
        return $this->marca . ' ' . $this->modelo;
    }
}

class CocheFactory
{
    public static function create($marca, $modelo)
    {
        return new Coche($marca, $modelo);
    }
}

$ibiza = CocheFactory::create('Seat', 'Ibiza');

Como podéis ver, en este código se utiliza un Factory para crear el objeto Coche. La gran ventaja de hacerlo a través de un Factory y no directamente es que en el caso de que tengas que modificar algo de la clase Coche, tan sólo tendrías que actualizar el Factory, y no las mil referencias que puedas tener en tu código. Este patrón consigue un acoplamiento muy bajo (interdependencia entre objetos).

¿Cuándo utilizar este patron? Si una clase tiene un número finito y conocido de objetos que debe crear, como por ejemplo una aplicación de el mundo, con distintos objetos para los continentes (finitos y conocidos) no sería aconsejado. En cambio, si la aplicación es sobre insectos del mundo, ya sabes de antemano que nuevas especies van a ser descubiertas, por tanto este patrón se ajustaría perfectamente.

2. Singleton

Cuando diseñamos aplicaciones web en muchas ocasiones interesa tener una sola instancia de una determinada clase. Un ejemplo claro serían objetos globales (como una clase con configuración), o un recurso compartido (como una "Cola de eventos").

En muchos casos se recomienda la inyección de dependencia (patrón en el que se suministran objetos a una clase en lugar de ser la propia clase quien cree el objeto) en lugar del patrón Singleton. Si estás pensando que el patrón de inyección de dependencia suena igual que el patrón Factory, es porque la diferencia entre ellas es bastante difusa.

La inyección de dependencia significa proporcionar los objetos que un objeto necesita (sus dependencias) via constructor, setters, o incluso mediante el framework que utilices, en lugar de hacer que los construya él mismo.

class Singleton
{
    private static $instance;
    
    public static function getInstance()
    {
        if (null === static::$instance) {
            static::$instance = new static();
        }        
        return static::$instance;
    }

    /**
     * Constructor para evitar la creación de una nueva instancia
     * a través del operador "new" desde fuera de esta clase
     */
    protected function __construct()
    {
    }

    /**
     * Método para prevenir la clonación de la instancia
     *
     * @return void
     */
    private function __clone()
    {
    }

    /**
     * Método para prevenir la deserialización de la instancia
     *
     * @return void
     */
    private function __wakeup()
    {
    }
}
$obj = Singleton::getInstance();

Patrones Estructurales

Estos patrones examinan como se componen los objetos y las clases. Usan herencia para crear interfaces. Entre ellos encontramos:

  • - Adapter (class y object)
  • - Bridge
  • - Composite
  • - Decorator
  • - Façade
  • - Flyweight
  • - Proxy

1. Composite

El patron Composite consiste en agrupar y gestionar grupos de objetos similares, de forma que su tratamiento no se distinga al de un objeto. Es un patrón sencillo pero puede llegar a confundir. Sirve para construir objetos complejos a partir de otros más simples y similares entre sí, gracias a la composición recursiva y a una estructura en forma de árbol

Un ejemplo claro es una lista de TO-DOs, cada elemento de la lista es una tarea, pero puede contener otras subtareas y así sucesivamente.


2. Decorator

Partamos de la base que tenemos un clase que no podemos modificar pero necesitamos introducir una nueva funcionalidad. El patrón Decorator propone crear una nueva clase que implemente esta interfaz envolviendo a la clase principal. Podemos tener varios clases Decorator para una misma clase, por lo que el diagrama nos quedaría así:


Donde ConcreteComponent es la clase principal que queremos modificar, la clase Decorator representa a todos los posibles Decorators, y finalmente Component es el resultado final.

class Book {
    private $author;
    private $title;
    function __construct($title_in, $author_in) {
        $this->author = $author_in;
        $this->title  = $title_in;
    }
    function getAuthor() {
        return $this->author;
    }
    function getTitle() {
        return $this->title;
    }
    function getAuthorAndTitle() {
      return $this->getTitle().' by '.$this->getAuthor();
    }
}

class BookTitleDecorator {
    protected $book;
    protected $title;
    public function __construct(Book $book_in) {
        $this->book = $book_in;
        $this->resetTitle();
    }   
    //doing this so original object is not altered
    function resetTitle() {
        $this->title = $this->book->getTitle();
    }
    function showTitle() {
        return $this->title;
    }
}

class BookTitleExclaimDecorator extends BookTitleDecorator {
    private $btd;
    public function __construct(BookTitleDecorator $btd_in) {
        $this->btd = $btd_in;
    }
    function exclaimTitle() {
        $this->btd->title = "!" . $this->btd->title . "!";
    }
}

class BookTitleStarDecorator extends BookTitleDecorator {
    private $btd;
    public function __construct(BookTitleDecorator $btd_in) {
        $this->btd = $btd_in;
    }
    function starTitle() {
        $this->btd->title = Str_replace(" ","*",$this->btd->title);
    }
}

$patternBook = new Book('Gamma, Helm, Johnson, and Vlissides', 'Design Patterns');
 
$decorator = new BookTitleDecorator($patternBook);
$starDecorator = new BookTitleStarDecorator($decorator);

Patrones de Comportamiento

Los patrones de comportamiento tratan sobre la comunicación entre objetos. Compuesto por los siguientes patrones:

  • - Chain of responsibility
  • - Command
  • - Interpreter
  • - Iterator
  • - Mediator
  • - Memento
  • - Null Object
  • - Observer
  • - State
  • - Strategy
  • - Template method
  • - Visitor

1. Strategy

Consiste en encapsular familias de algoritmos permitiendo a la clase responsable de instanciar un algoritmo especifico no necesitar tener conocimiento de la actual implementación.

Definir una familia de algoritmos, encapsula cada uno de ellos y los hace intercambiables. Es una estrategia que permite que al algoritmo variar dependiendo del uso que los clientes necesiten. Esa abstracción viene mediante una interfaz, y oculta la implementación de las clases derivadas.

interface OutputInterface{
    public function load();
}

class SerializedArrayOutput implements OutputInterface{
    public function load()
    {
        return serialize($arrayOfData);
    }
}

class JsonStringOutput implements OutputInterface{
    public function load()
    {
        return json_encode($arrayOfData);
    }
}

class ArrayOutput implements OutputInterface{
    public function load()
    {
        return $arrayOfData;
    }
}

class SomeClient{
    private $output;

    public function setOutput(OutputInterface $outputType)
    {
        $this->output = $outputType;
    }

    public function loadOutput()
    {
        return $this->output->load();
    }
}

$client = new SomeClient();

// Quieres un array?
$client->setOutput(new ArrayOutput());
$data = $client->loadOutput();

// Quieres un JSON?
$client->setOutput(new JsonStringOutput());
$data = $client->loadOutput();

Como acabas de ver existen muchísimos patrones de diseño, de hecho no deja de aparecer cada cierto tiempo alguno nuevo. Por eso vamos a terminar mencionando un par que no aparecen en la base de patrones de diseño, pero actualmente de los más usados en el mundo web.

1. Patrón Front Controller

Esté patrón es muy común en el mundo web ya que especifica una única entrada a una web (por ejemplo index.php) que se encarga de atender todas las peticiones. Este código es el responsable de cargar todas las dependencias y de procesar todas las peticiones, así como de generar las correspondientes respuestas.

2. Patrón Modelo-Controlador-Vista o MVC

Permite descomponer el código en tres objetos lógicos. El modelo es el encargado de acceder a la capa de datos (generalmente la base de datos). Los controladores gestionan las peticiones y procesan los datos del Modelo que cargan en las Vistas. Las Vistas son plantillas HTML que se devuelve al cliente.

Conceptos básicos de la POO en PHP

Me ha parecido interesante hacer un pequeño resumen de las distintas relaciones que podemos encontrar en la Programación Orientada a Objetos (POO) y su implementación en PHP. Seguro que muchos encontréis interesante este mini resumen del tema y los ejemplos que pongo.

Vamos a verlos gráficamente mediante su representación en UML y a continuación los describimos brevemente para después entender bien el código.

Estos son básicamente los tipos de relaciones que nos podemos encontrar aunque existen algunas más. Vamos a verlos:

Composición
Es una relación estructural donde los objetos relacionados tan sólo tienen sentido conjuntamente. El contenedor debe crearlos y contener un método que destruya los objetos que lo componen cuando este sea borrado, ya que estos carecen de sentido sin él.

class Ala {
    // Atributos y metodos
}

class Avion {

    private $alaIzquierda;
    private $alaDerecha;

    public function __construct() {
        $this->alaIzquierda = new Ala();
        $this->alaDerecha = new Ala();
    }

    function __destruct() {
        unset($this->alaDerecha);
        unset($this->alaIzquierda);
    }
}

$boing = new Avion();

Agregación
Es una relación estructural donde el contenedor mantiene un array o collection, etc. con los objetos que pertenecen a esta asociación. Debe contener a su vez un método para agregar nuevos objetos. En este caso la destrucción del contenedor no implica que los objetos agregados deban ser destruidos.

class Aeropuerto {
    // Atributos y metodos
}

class Ciudad {

    private $aeropuertos = array();

    function addAeropuerto( Aeropuerto $aeropuerto ) {
        array_push( $this->aeropuertos, $aeropuerto );
    }
}

$amsterdam = new Ciudad();
$amsterdam->addAeropuerto('Schipol');

Asociación
Relación estructural entre clases, en donde en ambas existe un atributo que referencia al otro. El tipo de atributo, si es sólo una instancia o un array, define la cardinalidad.

class Revista {
    private $personas = array();
}

class Persona {
    private $revistas = array();
}

$manu = new Persona();
$pcactual = new Revista();

Dependencia
Se trata de una relación de uso. Un clase utiliza a otra. Es por ejemplo si creamos un nuevo objeto con el operador "new" dentro de otra clase.

class Matematicas{
    function potencia(){
    }
    function raizCuadrada(){
    }
}
class Ecuacion{
    private $mates;
    
    function __construct(){
        $this->mates = new Matematicas();
    }
    
    function resuelve(){
    }
}

Herencia
Relación estructural donde una subclase hereda los atributos y los métodos de una Super Clase. Aunque las subclases pueden implementar sus propios métodos y clases. En los casos donde la superClase no tenga sentido por si sola esta se suele definirse abstracta. Luego lo vemos.

class Vehiculo
{
    private $ruedas;
    private $potencia;

    function avanza(){
 // Codigo
    }

    function gira(){
 // Codigo
    }
}

class Moto extends Vehiculo
{
    private $sidecar;

    function avanza(){
 // Overridden
    }
}

$yamaha = new Moto();

Servicios Web en PHP

Si hablamos de servicios web o web services, hablamos de interoperabilidad, es decir, intercambio de información entre aplicaciones independientemente del lenguaje en que se hayan desarrollado o las plataformas donde corran. Esto se consigue mediante el uso de determinados protocolos y estándares que veremos más adelante.

La información que proporcionan los servicios web se basa en texto, y el lenguaje utilizado es el XML y JSON. Lo que hace que el rendimiento de estos servicios no sea muy alto, aunque por otro lado además de la independencia entre aplicaciones y sistemas, nos permiten mantener intercambios de información sobre HTTP, lo cual es muy cómodo ya que no encontraremos problemas con firewalls.

Ejecutar ficheros SQL sobre MySQL desde línea de comandos

Después de intentar hacerlo ejecutar varios ficheros SQL desde el propio mysql sin éxito, he decidido hacer un mini script. Sinceramente creo que es la forma más sencilla para importar muchos ficheros sql directamente a la base de datos. Cread un script como el que veis con vuestros datos de conexión en la carpeta donde tengáis los ficheros sql y lanzarlo, no tiene más misterio.

Centrar un DIV absoluto

Hoy he tenido la necesidad de centrar un DIV absoluto y he dado con una solución muy interesante. La comparto por si le pudiera servir a alguien. Un saludo!

Un clasicazo ...

Envío de checkboxes o selector multiple por AJAX con jQuery

Pasar parámetros de un formulario mediante las llamadas ajax que proporciona jQuery resulta realmente sencillo. Pero ¿qué pasa si queremos enviar un conjunto de checkboxes? ¿y si queremos enviar varias opciones seleccionadas de un selector múltiple? Pues la solución es igual de sencilla: Serializar. Con una pequeña función podemos serializar estos datos para enviarlos todos juntos como parametro. Aquí podéis ver el código: