Está en la página 1de 19

Universidad Nacional de San Agustín

Facultad de Ingeniería de Producción y Servicios Escuela


Profesional de Ciencia de la Computación

Patrones de Diseño

ASIGNATURA: Ciencia de la Computación II


DOCENTE: Mamani Aliaga, Alvaro Henry

PERIODO ACADÉMICO: SEMESTRE 2022-A


GRUPO: A
INTEGRANTES: López Condori, Andrea del Rosario
Mena Quispe, Sergio Sebastian Santos
Carazas Quispe, Alessander Jesus
Gonzales Condori, Alejandro Javier
Yavar Guillen, Roberto Gustavo
Cerpa García, Randú Jean Franco
AREQUIPA - PERÚ

2022
1. BUILDER

Builder es un patrón de diseño creacional que nos


permite construir objetos complejos paso a paso. El
patrón nos permite producir distintos tipos y
representaciones de un objeto empleando el mismo
código de construcción.

Un patrón de diseño Builder distingue entre cuatro


actores:

● Director: este actor construye el objeto complejo con la interfaz del constructor. Es
consciente de los requisitos de secuencia del constructor. Con el director, se
desvincula la construcción del objeto del cliente.
● Builder: ofrece una interfaz para crear los componentes de un objeto (o producto)
complejo.
● Specific builder: crea las partes del objeto complejo, define (y gestiona) la
representación del object, y mantiene la interfaz de salida del objeto.
● Producto: es el resultado de la “actividad” del Builder Pattern, es decir, el objeto que
se construye.
El director supervisa el proceso decisivo del patrón Builder: la separación de la creación de
un objeto/producto del cliente.

Una manera de ilustrar el patrón de diseño Builder es considerando el ejemplo de un


restaurante al que un cliente realiza un pedido. Una vez que han recibido el pedido, los
cocineros actúan para elaborarlo. Todo el proceso hasta la entrega como tal se hace “entre
bambalinas”; el cliente no ve lo que ocurre en la cocina y solo recibe el resultado (el print en
lenguaje de programación).
Ventajas y desventajas
a) Ventajas

● Puedes construir objetos paso a paso, aplazar pasos de la construcción o ejecutar


pasos de forma recursiva.
● Puedes reutilizar el mismo código de construcción al construir varias
representaciones de productos.
● Principio de responsabilidad única. Puedes aislar un código de construcción
complejo de la lógica de negocio del producto.

b) Desventajas
● El patrón Constructor consta de un fuerte vínculo entre el producto, el constructor
específico y las clases del proceso de diseño, así que puede ser difícil hacer cambios
en el proceso básico. La construcción de los objetos requiere conocer su uso y su
entorno concretos. Utilizar patrones conocidos, como el patrón de diseño Builder,
puede hacer que los programadores pasen por alto soluciones más sencillas y
elegantes. En el fondo, muchos desarrolladores consideran que este es uno de los
patrones de diseño menos importantes.
● La complejidad general del código aumenta, ya que el patrón exige la creación de
varias clases nuevas.

Relaciones con otros patrones


● Muchos diseños empiezan utilizando el Factory Method (menos complicado y más
personalizable mediante las subclases) y evolucionan hacia Abstract Factory,
Prototype, o Builder (más flexibles, pero más complicados).
● Builder se enfoca en construir objetos complejos, paso a paso. Abstract Factory se
especializa en crear familias de objetos relacionados. Abstract Factory devuelve el
producto inmediatamente, mientras que Builder te permite ejecutar algunos pasos
adicionales de construcción antes de extraer el producto.
● Puedes utilizar Builder al crear árboles Composite complejos porque puedes
programar sus pasos de construcción para que funcionen de forma recursiva.
● Puedes combinar Builder con Bridge: la clase directora juega el papel de la
abstracción, mientras que diferentes constructoras actúan como implementaciones.
● Los patrones Abstract Factory, Builder y Prototype pueden todos ellos
implementarse como Singletons.
1.1 IMPLEMENTACIÓN

● Builder
#ifndef BUILDER
#define BUILDER
class Builder
{
public:
virtual ~Builder(){}
virtual void ProducirParedes() const = 0;
virtual void ProducirColumnas() const = 0;
virtual void ProducirTecho() const = 0;
virtual void ProducirPuertas() const = 0;
virtual void ProducirVentanas() const = 0;
};

#endif

● Builder Especifico
#ifndef BUILDER_ESPECIFICO
#define BUILDER_ESPECIFICO

#include"Producto.h"
#include"Builder.h"

class BuilderEspecifico : public Builder


{
private:
Producto * product;
public:
BuilderEspecifico(){
this->Reset();
}

~BuilderEspecifico(){
delete product;
}

void Reset(){
this->product = new Producto();
}

void ProducirParedes() const override {


this->product->componentes.push_back("Paredes");
}
void ProducirColumnas() const override {
this->product->componentes.push_back("Columnas");
}
void ProducirTecho() const override {
this->product->componentes.push_back("Techo");
}

void ProducirPuertas() const override {


this->product->componentes.push_back("Puertas");
}

void ProducirVentanas() const override {


this->product->componentes.push_back("Ventanas");
}

Producto * getProducto(){
Producto * resultado = this->product;
this->Reset();
return resultado;
}
};

#endif
● Director
#ifndef DIRECTOR
#define DIRECTOR
#include"Builder.h"

class Director {
private:
Builder* builder;
public:
void setBuilder(Builder* builder){
this->builder = builder;
}

void ConstruirCimientos(){
this->builder->ProducirParedes();
this->builder->ProducirColumnas();
}

void ConstruirCasaTechada(){
ConstruirCimientos();
this->builder->ProducirTecho();
}

void ConstruirCasaTerminada(){
ConstruirCasaTechada();
this->builder->ProducirPuertas();
this->builder->ProducirVentanas();
}
};

#endif

● Main
#include<iostream>
using namespace std;

#include"Director.h"
#include"Builder.h"
#include"BuilderEspecifico.h"

void construcciones(Director &director){


BuilderEspecifico* builder = new BuilderEspecifico();
director.setBuilder(builder);

cout<<"Cimientos de la casa: "<<endl;


director.ConstruirCimientos();
Producto * p = builder->getProducto();
p->ListaComp();
delete p;

cout<<endl<<"Casa techada: "<<endl;


director.ConstruirCasaTechada();
p = builder->getProducto();
p->ListaComp();
delete p;

cout<<endl<<"Casa terminada: "<<endl;


director.ConstruirCasaTerminada();
p = builder->getProducto();
p->ListaComp();
delete p;
}

int main(){

Director * director = new Director;


construcciones(*director);
delete director;

return 0;
}

1.2 CAPTURAS
2. ABSTRACT FACTORY

El patrón de diseño Abstract Factory busca agrupar un conjunto


de clases que tiene un funcionamiento en común llamadas
familias, las cuales son creadas mediante un Factory, este patrón
es especialmente útil cuando requerimos tener ciertas familias de
clases para resolver un problema, sin embargo, puede que se
requieran crear implementaciones paralelas de estas clases para
resolver el mismo problema pero con una implementación
distinta.

La estructura de Abstract Factory puede resultar muy enredosa ya que tiene muchos
componentes y éstos aparentemente se mezclan entre sí. Para comprender mejor cómo
funciona este patrón explicaremos cada componente:

● Client: Representa la persona o evento que dispara la ejecución del patrón.


● AbstractProduct (A, B): Interfaces que definen la estructura de los objetos para crear
familias.
● ConcreteProduct (A, B): Clases que heredan de AbstractProduct con el fin de
implementar familias de objetos concretos.
● ConcreteFactory: Representan las fábricas concretas que servirán para crear las
instancias de todas las clases de la familia. En esta clase debe existir un método para
crear cada una de las clases de la familia.
● AbstractFactory: Define la estructura de las fábricas y deben proporcionar un
método para cada clase de la familia.
Utiliza el patrón Abstract Factory cuando tu código deba funcionar con varias familias de
productos relacionados, pero no desees que dependa de las clases concretas de esos
productos, ya que puede ser que no los conozcas de antemano o sencillamente quieras
permitir una futura extensibilidad.

El patrón Abstract Factory nos ofrece una interfaz para crear objetos a partir de cada clase de
la familia de productos. Mientras tu código cree objetos a través de esta interfaz, no tendrás
que preocuparte por crear la variante errónea de un producto que no combine con los
productos que ya ha creado tu aplicación.

● Considera la implementación del patrón Abstract Factory cuando tengas una clase con
un grupo de métodos de fábrica que nublen su responsabilidad principal.
● En un programa bien diseñado cada clase es responsable tan solo de una cosa.
Cuando una clase lidia con varios tipos de productos, puede ser que valga la pena
extraer sus métodos de fábrica para ponerlos en una clase única de fábrica o una
implementación completa del patrón Abstract Factory.

Ventajas y desventajas
a) Ventajas
Puedes tener la certeza de que los productos que obtienes de una fábrica son
compatibles entre sí.
Evitas un acoplamiento fuerte entre productos concretos y el código cliente.
Principio de responsabilidad única. Puedes mover el código de creación de productos
a un solo lugar, haciendo que el código sea más fácil de mantener.
Principio de abierto/cerrado. Puedes introducir nuevas variantes de productos sin
descomponer el código cliente existente.

b) Desventajas
Puede ser que el código se complique más de lo que debería, ya que se introducen
muchas nuevas interfaces y clases junto al patrón.

2.1 IMPLEMENTACIÓN

● Abstract Factory
● Abstract Modern Product

● Abstract Old Product


● Modern Products

● Old Products
● Factories

Enlace
3. PROTOTYPE

El patrón de diseño prototype tiene como


finalidad crear nuevos objetos clonando
una instancia creada previamente. Este
patrón especifica la clase de objetos a
crear mediante la clonación de un prototipo
que es una instancia ya creada. La clase
de los objetos que servirán de prototipo
deberá incluir en su interfaz la manera de
solicitar una copia, que será desarrollada
luego por las clases concretas de
prototipos.

3.1 USO
Este patrón resulta útil en escenarios
donde es impreciso abstraer la lógica que
decide qué tipos de objetos utilizará una aplicación, de la lógica que luego usarán esos
objetos en su ejecución. Los motivos de esta separación pueden ser variados, como por
ejemplo:
● La aplicación debe basarse en alguna configuración o parámetro en tiempo de
ejecución para decidir el tipo de objetos que se debe crear.
● En ese caso, la aplicación necesitará crear nuevos objetos a partir de modelos.
● El modelo o prototipo es clonado y el nuevo objeto será una copia exacta de los mismos,
con el mismo estado.

3.2 Participantes
● Cliente: es el encargado de solicitar la creación de los nuevos objetos a partir de los
prototipos.
● Prototipo Concreto: posee características concretas que serán reproducidas para
nuevos objetos e implementa una operación para clonarse.
● Prototipo: declara una interfaz para clonarse, a la que accede el cliente.
El cliente solicita al prototipo que se clone.
3.3 Aplicabilidad

● Utiliza el patrón Prototype cuando tu código no deba depender de las clases


concretas de objetos que necesites copiar.
● Esto sucede a menudo cuando tu código funciona con objetos pasados por código
de terceras personas a través de una interfaz. Las clases concretas de estos objetos
son desconocidas y no podrías depender de ellas aunque quisieras.
● El patrón Prototype proporciona al código cliente una interfaz general para trabajar
con todos los objetos que soportan la clonación. Esta interfaz hace que el código
cliente sea independiente de las clases concretas de los objetos que clona.
● Utiliza el patrón cuando quieras reducir la cantidad de subclases que solo se
diferencian en la forma en que inicializan sus respectivos objetos. Puede ser que
alguien haya creado estas subclases para poder crear objetos con una configuración
específica.
● El patrón Prototype te permite utilizar como prototipos un grupo de objetos
prefabricados, configurados de maneras diferentes.
● En lugar de instanciar una subclase que coincida con una configuración, el cliente
puede, sencillamente, buscar el prototipo adecuado y clonarlo.
3.4 Ventajas y desventajas

Ventajas

● Puedes clonar objetos sin acoplarlos a sus clases concretas.


● Puedes evitar un código de inicialización repetido clonando prototipos
prefabricados.
● Puedes crear objetos complejos con más facilidad.
● Obtienes una alternativa a la herencia al tratar con pre-ajustes de configuración
para objetos complejos.

Desventajas

● Clonar objetos complejos con referencias circulares puede resultar


complicado.

3.5 Implementación
4. Singleton

Patrón de diseño creacional que nos permite asegurarnos de que una clase tenga una única
instancia, a la vez que proporciona un punto de acceso global a dicha instancia.

El patrón Singleton resuelve dos problemas al mismo tiempo, vulnerando el Principio de


responsabilidad única:
1. Garantizar que una clase tenga una única instancia.
Controla el acceso a algún recurso compartido, por ejemplo, una base de datos o un
archivo.
Funciona así: imagina que has creado un objeto y al cabo de un tiempo decides crear otro
nuevo. En lugar de recibir un objeto nuevo, obtendrás el que ya habías creado.
Teniendo en cuenta que este comportamiento es imposible de implementar con un
constructor normal, ya que una llamada al constructor siempre debe devolver un nuevo
objeto por diseño.

2. Proporcionar un punto de acceso global a dicha instancia

Singleton nos permite acceder a un objeto desde cualquier parte del programa. No obstante,
también evita que otro código sobreescriba esa instancia.
Este problema tiene otra cara: no queremos que el código que resuelve el primer problema
se encuentre disperso por todo el programa. Es mucho más conveniente tenerlo dentro de
una clase, sobre todo si el resto del código ya depende de ella.
Hoy en día el patrón Singleton se ha popularizado tanto que la gente suele llamar singleton
a cualquier patrón, incluso si sólo resuelve uno de los problemas antes mencionados.

Todas las implementaciones del patrón Singleton tienen estos dos pasos en común:
● Hacer privado el constructor por defecto para evitar que otros objetos utilicen el
operador new con la clase Singleton.
● Crear un método de creación estático que actúe como constructor. Tras bambalinas,
este método invoca al constructor privado para crear un objeto y lo guarda en un campo
estático. Las siguientes llamadas a este método devuelven el objeto almacenado en
caché.
Si tu código tiene acceso a la clase Singleton, podrá invocar su método estático. De esta
manera, cada vez que se invoque este método, siempre se devolverá el mismo objeto.
IMPLEMENTACIÓN

#include <iostream>

using namespace std;

class Singleton
{

protected:
Singleton() = default;

public:

int data;

static Singleton& get_instance()


{
static Singleton instance;
return instance;
}

Singleton(const Singleton&) = delete;


Singleton(Singleton&&) = delete;
Singleton& operator=(const Singleton&) = delete;
Singleton& operator=(Singleton&&) = delete;

};

int main()
{

Singleton &singleton1 = Singleton::get_instance();

singleton1.data = 20;

cout << "singleton1.data = " << singleton1.data <<


endl;

Singleton &singleton2 = Singleton::get_instance();


cout << "singleton2.data = " << singleton2.data <<
endl;
//imprime lo mismo debido a que es una referencia al
mismo objeto de memoria

Singleton::get_instance().data = 50;

cout << "data: " << Singleton::get_instance().data


<< endl;

cout << "singleton1.data = " << singleton1.data <<


endl;
cout << "singleton2.data = " << singleton2.data <<
endl;

//Singleton singletonN = singleton1;

//singletonN.data = 100;

//cout << "singleton1.data = " << singleton1.data <<


endl;
//cout << "singletonN.data = " << singletonN.data <<
endl;

return 0;
}

También podría gustarte