Patrones de diseño | Conjunto 2 (método de fábrica)

El método fábrica es un patrón de diseño creacional , es decir, relacionado con la creación de objetos. En el patrón Factory, creamos objetos sin exponer la lógica de creación al cliente y el cliente usa la misma interfaz común para crear un nuevo tipo de objeto. 

La idea es usar una función de miembro estática (método de fábrica estático) que crea y devuelve instancias, ocultando los detalles de los módulos de clase del usuario.
Un patrón de fábrica es uno de los principios básicos de diseño para crear un objeto, lo que permite a los clientes crear objetos de una biblioteca (se explica a continuación) de tal manera que no tenga un vínculo estrecho con la jerarquía de clases de la biblioteca.

¿A qué nos referimos cuando hablamos de bibliotecas y clientes?  
Una biblioteca es algo proporcionado por un tercero que expone algunas API públicas y los clientes hacen llamadas a esas API públicas para completar sus tareas. Un ejemplo muy simple pueden ser los diferentes tipos de vistas proporcionadas por el sistema operativo Android. 
 
¿Por qué patrón de fábrica?  
Entendámoslo con un ejemplo: 

C++

// A design without factory pattern
#include <iostream>
using namespace std;
 
// Library classes
class Vehicle {
public:
    virtual void printVehicle() = 0;
};
class TwoWheeler : public Vehicle {
public:
    void printVehicle()
    {
        cout << "I am two wheeler" << endl;
    }
};
class FourWheeler : public Vehicle {
public:
    void printVehicle()
    {
        cout << "I am four wheeler" << endl;
    }
};
 
// Client (or user) class
class Client {
public:
    Client(int type)
    {
 
        // Client explicitly creates classes according to
        // type
        if (type == 1)
            pVehicle = new TwoWheeler();
        else if (type == 2)
            pVehicle = new FourWheeler();
        else
            pVehicle = NULL;
    }
 
    ~Client()
    {
        if (pVehicle) {
            delete[] pVehicle;
            pVehicle = NULL;
        }
    }
 
    Vehicle* getVehicle() { return pVehicle; }
 
private:
    Vehicle* pVehicle;
};
 
// Driver program
int main()
{
    Client* pClient = new Client(1);
    Vehicle* pVehicle = pClient->getVehicle();
    pVehicle->printVehicle();
    return 0;
}
Producción

I am two wheeler

¿Cuáles son los problemas con el diseño anterior?  
Como debe haber observado en el ejemplo anterior, el Cliente crea objetos de TwoWheeler o FourWheeler en función de alguna entrada durante la construcción de su objeto. 
Digamos que la biblioteca presenta una nueva clase ThreeWheeler para incorporar también vehículos de tres ruedas. ¿Qué pasaría? El cliente terminará enstringndo un nuevo else si en la escalera condicional para crear objetos de ThreeWheeler. Lo que a su vez necesitará que el Cliente sea recompilado. Por lo tanto, cada vez que se realiza un nuevo cambio en el lado de la biblioteca, el Cliente deberá realizar algunos cambios correspondientes al final y volver a compilar el código. ¿Suena mal? Esta es una muy mala práctica de diseño.

¿Cómo evitar el problema?  
La respuesta es crear un método estático (o de fábrica). Veamos el siguiente código.

C++

// C++ program to demonstrate factory method design pattern
#include <iostream>
using namespace std;
 
enum VehicleType {
    VT_TwoWheeler,    VT_ThreeWheeler,    VT_FourWheeler
};
 
// Library classes
class Vehicle {
public:
    virtual void printVehicle() = 0;
    static Vehicle* Create(VehicleType type);
    virtual ~Vehicle(){}
};
class TwoWheeler : public Vehicle {
public:
    void printVehicle() {
        cout << "I am two wheeler" << endl;
    }
};
class ThreeWheeler : public Vehicle {
public:
    void printVehicle() {
        cout << "I am three wheeler" << endl;
    }
};
class FourWheeler : public Vehicle {
    public:
    void printVehicle() {
        cout << "I am four wheeler" << endl;
    }
};
 
// Factory method to create objects of different types.
// Change is required only in this function to create a new object type
Vehicle* Vehicle::Create(VehicleType type) {
    if (type == VT_TwoWheeler)
        return new TwoWheeler();
    else if (type == VT_ThreeWheeler)
        return new ThreeWheeler();
    else if (type == VT_FourWheeler)
        return new FourWheeler();
    else return NULL;
}
 
// Client class
class Client {
public:
 
    // Client doesn't explicitly create objects
    // but passes type to factory method "Create()"
    Client()
    {
        VehicleType type = VT_ThreeWheeler;
        pVehicle = Vehicle::Create(type);
    }
    ~Client() {
        if (pVehicle) {
            delete pVehicle;
            pVehicle = NULL;
        }
    }
    Vehicle* getVehicle()  {
        return pVehicle;
    }
 
private:
    Vehicle *pVehicle;
};
 
// Driver program
int main() {
    Client *pClient = new Client();
    Vehicle * pVehicle = pClient->getVehicle();
    pVehicle->printVehicle();
      delete pClient;
    return 0;
}
Producción

I am three wheeler

En el ejemplo anterior, hemos desacoplado totalmente la selección de tipos para la creación de objetos del Cliente. La biblioteca ahora es responsable de decidir qué tipo de objeto crear en función de una entrada. El cliente solo necesita realizar llamadas al método Create de fábrica de la biblioteca y pasar el tipo que desea sin preocuparse por la implementación real de la creación de objetos.

Otros ejemplos del Método de Fábrica: 

  1. Digamos, en un sistema de ‘Dibujo’, dependiendo de la entrada del usuario, se pueden dibujar diferentes imágenes como cuadrados, rectángulos, el círculo. Aquí podemos usar el método de fábrica para crear instancias según la entrada del usuario. Para agregar un nuevo tipo de forma, no es necesario cambiar el código del cliente.
  2. Otro ejemplo: en el sitio de viajes, podemos reservar billetes de tren, así como billetes de autobús y billetes de avión. En este caso, el usuario puede indicar su tipo de viaje como ‘autobús’, ‘tren’ o ‘vuelo’. 
    Aquí tenemos una clase abstracta ‘AnyTravel’ con una función miembro estática ‘GetObject’ que, según el tipo de viaje del usuario, creará y devolverá un objeto de ‘BusTravel’ o ‘TrainTravel’. ‘BusTravel’ o ‘TrainTravel’ tienen funciones comunes como el nombre del pasajero, el origen y los parámetros de destino.

Lectura adicional: Método de fábrica en Python
Escriba comentarios si encuentra algo incorrecto o si desea compartir más información sobre el tema tratado anteriormente.

Publicación traducida automáticamente

Artículo escrito por GeeksforGeeks-1 y traducido por Barcelona Geeks. The original can be accessed here. Licence: CCBY-SA

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *