Patrones de diseño: Factory

El patrón factory es un patrón de diseño creacional que provee una interfaz de creación de objetos en una superclase, pero permitiendo a las subclases alterar el tipo del objeto a crear.

Este patrón de diseño sugiere reemplazar la creación directa de objetos (usando el operador new en muchos lenguajes) con llamadas al método factory.

Aterricemos el concepto con un ejemplo clásico: una interfaz de usuario. Esta interfaz tiene diferentes implementaciones de los componentes dependiendo de la plataforma.

¿Cómo creamos un nuevo componente que sea independiente de la plataforma?

Patrón Factory al rescate.

  • Declaramos una interfaz común a todos los componentes
class Button {
    public:
        virtual ~Button() {};
        virtual void render() = 0;
};
  • Creamos la implementación concreta de todos los componentes
class LinuxButton: public Button {
    public:
        ~LinuxButton() {};
        void render() {
            cout << "LinuxButton" << endl;
        }
};

class WindowsButton: public Button {
    public:
        ~WindowsButton() {};
        void render() {
            cout << "WindowsButton" << endl;
        }
};
  • Declaramos la factoría abstracta constructora de componentes.
class ButtonFactory {
    public:
        virtual Button* createButton() = 0;
};
  • Creamos las factorías abstractas
class LinuxButtonFactory {
    public:
        LinuxButton* createButton() {
            return new LinuxButton();
        }
}
class WindowsButtonFactory {
    public:
        WindowsButton* createButton() {
            return new WindowsButton();
        }
}
  • Código cliente
class GUI {
    public:
        GUI(ButtonFactory &buttonFactory): buttonFactory(&buttonFactory) {};
        void render() {
            Button* button = this->buttonFactory->createButton();
            button->render();
            delete button;
        }
    private:
        ButtonFactory *buttonFactory;
};

int main(void) {
    LinuxButtonFactory linuxButtonFactory;
    GUI linuxGUI(linuxButtonFactory);
    linuxGUI.render();
    
    WindowsButtonFactory windowsButtonFactory;
    GUI windowsGUI(windowsGUI);
    windowsGUI.render();
    return 0;
}

Ventajas

  • Rompe la dependencia entre clases. La clase cliente no depende de una clase concreta sino de una interfaz.
  • Fácil extensión del código. Basta con extender la clase Factory y devolver nuevos subtipos, sin romper el código cliente.

Inconvenientes

  • Añade complejidad porque hay que agregar varias subclases para implementar el patrón.