Patrones de diseño en PHP. Patrón Builder


Patrones de diseño en PHP. Patrón Builder

Construir de manera flexible un objeto complejo a partir de otro objeto complejo en una serie de pasos.



El patrón de diseño Builder es un patrón de creación que le permite separar la construcción de objetos complejos de sus representaciones para que el mismo proceso de construcción pueda crear representaciones diferentes.

Repaso

Se ha estado trabajando en los artículos anteriores en un catalogo de libros referente a una pequeña biblioteca en la cual hemos distinguido dos editoriales (OReilly y Sams) y dos temáticas (PHP y MySQL). Nuestro catalogo nos entrega un libro cualquiera como resultado de esas combinaciones (no hemos planteado la lógica de un buscador especifico pues es indiferente el como se obtienen los libros). Lo anterior nos permite identificar al menos tres objetos finales, el objeto editorial, el objeto temática y el objeto libro.
Cada uno de estos objetos implementa métodos comunes definidos en una Interface
Sin embargo en los artículos anteriores se ha visto que agregar una nueva editorial o nueva temática, e incluso un libro cualquiera, implica un cambio en el código y con ello violamos el principio open/closed que nos invita a extender, no a modificar el código ya escrito.
El patrón Builder nos guía en la elaboración de una clase Director que maneja la construcción de objetos finales (los libros) y con esto poder agregar nuevos objetos fácilmente según hayamos descrito en los métodos de la clase Director.

Propósito

Construir de manera flexible un objeto complejo a partir de otro objeto complejo en una serie de pasos.

Motivación

Estructura de nuestro ejemplo aplicado el patrón Builder
Estructura de nuestro ejemplo aplicado el patrón Builder
Implementar una clase Director con diferentes métodos para la construcción de un objeto libro y que como resultado devuelva un objeto armado.
En nuestro diagrama debemos notar una nueva clase CSharp que hace referencia a libros relacionados con el lenguaje de programación C# pero que no le hemos dado una temática y editorial especifica.

Aplicabilidad

Cuando el algoritmo de un objeto completo (nuestros libros) debiera ser independiente de las partes (editorial y temática) de que se compone dicho objeto y como se arma.
O bien, el proceso de construcción debe permitir diferentes representaciones del objeto que esta siendo construido.

Estructura y participantes

Diagrama patrón Builder según GoF
Diagrama patrón Builder según GoF
  • Los Constructores (Las interfaces para tematica, editoriales o libros) que definen métodos abstractos para los objetos
  • Los ConstructoresConcretos (Temática y Editorial) que serian las clases que implementan las interfaces
  • El Director que construye los objetos según una interface
  • y los Productos que serian las clases finales


Colaboraciones

  • El Cliente (Catalogo) crea el objeto Director y lo configura con el objeto Constructor (Libro) deseado
  • El Director notifica al constructor cada vez que hay que construir una parte del producto (editorial y temática)
  • El constructor maneja las peticiones del Director y son añadidas al producto
  • El Cliente obtiene el producto (Libro) del constructor.


Consecuencias

Podemos permitir que los libros varíen en su formato interno sin modificar la interface que los definen, agregando el numero de paginas por ejemplo.
Aislamos el código de construcción y representación definiendo interfaces.
Nos permite la construcción de diferentes Directores según que objeto necesitemos.
Nos otorga un control sobre el proceso de creación de un objeto Libro.

Implementación

Se debe implementar una clase o interface abstracta por cada uno de los objetos finales a construir, así tenemos a la interface ITematicaBuilder para el objeto final Temática, la interface IEditorialBuilder para el objeto final Editorial, e ILibroBuilder para los libros finales.

Código de Ejemplo

La clase importante en este patrón es la clase Director y su código sera:
/* Clase Director */
namespace Blockpc\Clases;
use Blockpc\Patrones\Builder\ITematicaBuilder;
use Blockpc\Patrones\Builder\IEditorialBuilder;
class Director {
private $editorial;
private $tematica;
private $clase;
public function editorial(IEditorialBuilder $editorial) {
$editorial->setEditorial();
$this->editorial = $editorial->getEditorial();
}
public function tematica(ITematicaBuilder $tematica) {
$tematica->setTematica();
$this->tematica = $tematica->getTematica();
}
public function soloLibro($clase) {
$this->editorial = false;
$this->tematica = false;
$this->clase = $clase;
}
public function buildLibro() {
$clase = $this->clase ?? $this->editorial->name . $this->tematica->name;
$claseLibro = "Blockpc\\Patrones\\Builder\\Libros\\{$clase}";
if (!class_exists($claseLibro)) {
throw new \Exception("la clase {$this->clase} no existe!");
}
$libro = new $claseLibro;
$editorial = !$this->editorial ? "No especificada" : $this->editorial->name;
$tematica = !$this->tematica ? "No especificada" : $this->tematica->name;
$libro->setEditorial($editorial);
$libro->setTematica($tematica);
return $libro;
}
}
Los tres primeros métodos actúan como constructores propios en el sentido de que estos métodos definirán como se construye el libro final en el método buildLibro()
Nuestro Catalogo es quien crea un objeto Director y que muestra el libro creado por este.
/* Clase Catalogo */
namespace Blockpc\Clases;
use Blockpc\Patrones\Builder\ITematicaBuilder;
use Blockpc\Patrones\Builder\IEditorialBuilder;
final class Catalogo {
private $editorial;
private $tematica;
private $director;
private $libro;
public function __construct() {
echo "Iniciando Catalogo...<br>";
}
public function armadoLibro(string $editorialBuscada, string $tematicaBuscada = '') {
$this->director = new Director;
$editorial = "Blockpc\\Patrones\\Builder\\Editoriales\\{$editorialBuscada}";
$tematica = "Blockpc\\Patrones\\Builder\\Tematicas\\{$tematicaBuscada}";
$this->editorial = (class_exists($editorial)) ? new $editorial : $editorialBuscada;
$this->tematica = (class_exists($tematica)) ? new $tematica : $tematicaBuscada;
if ( ($this->editorial instanceof IEditorialBuilder) and ($this->tematica instanceof ITematicaBuilder) ) {
$this->director->editorial($this->editorial);
$this->director->tematica($this->tematica);
} else {
$this->director->soloLibro($editorialBuscada);
}
$this->libro = $this->director->buildLibro();
}
public function mostrarLibro() {
$this->escribir($this->libro->getEditorial());
$this->escribir($this->libro->getTematica());
$this->escribir($this->libro->getTitulo());
$this->escribir($this->libro->getAutor());
$this->escribir('');
}
private function escribir(String $linea): void {
echo $linea."<br/>";
}
public function __destruct() {
echo "... Catalogo completado.<br>";
}
}
El Catalogo es quien le debe pasar al Director la información necesaria para crear el libro. El primer método armadoLibro() recibe dos parámetros como string, siendo el segundo opcional, el primero indica la editorial y el segundo la temática, entonces valida y entrega estos datos al Director y recibe el libro creado por este. El segundo método mostrarLibro() muestra el libro.
En nuestro index.php iniciamos un objeto Catalogo y hacemos el armado de libro y los mostramos
$catalogo = new Catalogo;
/* Construcción del libro pedido usando OReilly y PHP*/
$catalogo->armadoLibro("OReilly", "PHP");
/* mostramos el libro pedido */
$catalogo->mostrarLibro();
/* Construcción del libro pedido solo CSharp */
$catalogo->armadoLibro("CSharp");
/* mostramos el libro pedido */
$catalogo->mostrarLibro();
Las demás clases no cambian mucho de los artículos anteriores y el código estará para descarga al final del articulo.

Usos Conocidos

En el libro podemos encontrar algunos usos mas o menos antiguos jeje, sin embargo este patrón se puede usar en cualquier sistema que requiera que la creación de objetos sea indiferente de como son representados.

Patrones Relacionados

EL patrón Abstract factory también construye objetos complejos pero Builder lo realiza paso a paso, ademas el patrón Abstract factory esta centrado en familias de objetos devolviendo uno de estos inmediatamente.
Existe una relación con el patrón Composite que se vera mas adelante.

Ultimas Consideraciones

Debemos tener en cuenta que nuestro catalogo no es el mejor ejemplo para mostrar la aplicabilidad de este patrón. Se debería construir un Director por cada interface que defina la creación de un objeto final especifico y reunir todos estos resultados para obtener el libro final.
El código fuente lo puedes descargar desde acá.
Saludos y hasta el próximo articulo.

Etiquetas patrones diseño solid

Ultima actualización Sábado 19 de Enero, 2019




Agregar Comentario