9.11.2. Factorías abstractas

El patrón Factoría Abstracta se parece a las factorías que hemos visto anteriormente, pero con varios Factory Methods. Cada uno de los Factory Method crea una clase distinta de objeto. Cuando se crea el objecto factoría, se decide cómo se usarán todos los objetos creados con esa factoría. El ejemplo del GoF implementa la portabilidad a través de varias interfaces gráficas de usuario (GUI): se crea el objeto factoría apropiado para la GUI con la que se está trabajando y desde ahí en adelante, cuando le pida un menú, botón, barra deslizante y demás, creará automáticamente la versión apropiada para la GUI de ese elemento. Por lo tanto, es posible aislar, en un solo lugar, el efecto de cambiar de una GUI a otra.

Por ejemplo, suponga que está creando un entorno para juegos de propósito general y quiere ser capaz de soportar diferentes tipos de juegos. Así es como sería usando una Factoría Abstracta:

//: C10:AbstractFactory.cpp
// A gaming environment.
#include <iostream>
using namespace std;

class Obstacle {
public:
  virtual void action() = 0;
};

class Player {
public:
  virtual void interactWith(Obstacle*) = 0;
};

class Kitty: public Player {
  virtual void interactWith(Obstacle* ob) {
    cout << "Kitty has encountered a ";
    ob->action();
  }
};

class KungFuGuy: public Player {
  virtual void interactWith(Obstacle* ob) {
    cout << "KungFuGuy now battles against a ";
    ob->action();
  }
};

class Puzzle: public Obstacle {
public:
  void action() { cout << "Puzzle" << endl; }
};

class NastyWeapon: public Obstacle {
public:
  void action() { cout << "NastyWeapon" << endl; }
};

// The abstract factory:
class GameElementFactory {
public:
  virtual Player* makePlayer() = 0;
  virtual Obstacle* makeObstacle() = 0;
};

// Concrete factories:
class KittiesAndPuzzles : public GameElementFactory {
public:
  virtual Player* makePlayer() { return new Kitty; }
  virtual Obstacle* makeObstacle() { return new Puzzle; }
};

class KillAndDismember : public GameElementFactory {
public:
  virtual Player* makePlayer() { return new KungFuGuy; }
  virtual Obstacle* makeObstacle() {
    return new NastyWeapon;
  }
};

class GameEnvironment {
  GameElementFactory* gef;
  Player* p;
  Obstacle* ob;
public:
  GameEnvironment(GameElementFactory* factory)
  : gef(factory), p(factory->makePlayer()),
    ob(factory->makeObstacle()) {}
  void play() { p->interactWith(ob); }
  ~GameEnvironment() {
    delete p;
    delete ob;
    delete gef;
  }
};

int main() {
  GameEnvironment
    g1(new KittiesAndPuzzles),
    g2(new KillAndDismember);
  g1.play();
  g2.play();
}
/* Output:
Kitty has encountered a Puzzle
KungFuGuy now battles against a NastyWeapon */ ///:~

Listado 9.25. C10/AbstractFactory.cpp


En este entorno, los objetos Player interactúan con objetos Obstacle, pero los tipos de los jugadores y los obstáculos dependen del juego. El tipo de juego se determina eligiendo un GameElementFactory concreto, y luego el GameEnvironment controla la configuración y ejecución del juego. En este ejemplo, la configuración y ejecución son simples, pero dichas actividades (las condiciones iniciales y los cambios de estado) pueden determinar gran parte del resultado del juego. Aquí, GameEnvironment no está diseñado para ser heredado, aunque puede tener sentido hacerlo.

Este ejemplo también ilustra el despachado doble, que se explicará más adelante.