(learn&think)

不浮躁,不自傲,学习,思考,总结

浅谈设计模式五: 抽象工厂模式(Abstract Factory)

| Comments

1 实例

继续扩展模式四中的批萨店实例,加入批萨配料对象。不同的地区的批萨店有一组共同的产品(如生面,酱汁,芝士等等),但是根据不同地区的口味有不同的实现。整个系统变得有点复杂了,

  1. 有不同的批萨原料,同样的批萨原料在不同的地区有不同的作法;
  2. 每个店有不同种类的批萨(即各种原料搭配不同);
  3. 整个批萨连锁在不同地区有不同的店。

如何清晰而易维护的设计整个批萨连锁店系统呢?

2 抽象工厂模式(Abstract Factory)

2.1 目的

为创建一组相关或相依赖的对象提供一个接口,而不用指定它们具体的类。

2.2 实现

2.2.1 定义各个原料的接口

比如 Douge

class Dough {
 public:
  virtual string toString() = 0;
};

比如 Sauce

class Sauce {
 public:
  virtual string toString() = 0;
};

其他原料等。

2.2.2 根据原料接口定义原料

比如 Douge

class ThickCrustDough : public Dough {
 public:
  string toString();
};
class ThinCrustDough : public Dough {
 public:
  string toString();
};

2.2.3 定义批萨原料创建抽象工厂接口

class PizzaIngredientFactory {
 public:
  virtual ~PizzaIngredientFactory() {}
  virtual Dough* createDough() = 0;
  virtual Sauce* createSauce() = 0;
  virtual Cheese* createCheese() = 0;
  virtual Clams* createClam() = 0;
}

2.2.4 根据原料抽象工厂接口不同地区定义不同原料

比如 Chicago

Dough* ChicagoPizzaIngredientFactory::createDough() {
  return new ThickCrustDough();
}

Sauce* ChicagoPizzaIngredientFactory::createSauce() {
  return new PlumTomatoSauce();
}

Cheese* ChicagoPizzaIngredientFactory::createCheese() {
  return new MozzarellaCheese();
}

Clams* ChicagoPizzaIngredientFactory::createClam() {
  return new FrozenClams();
}

2.2.5 定义不同批萨的接口

class Pizza {
 public:
  Pizza();
  virtual ~Pizza();
  string name();
  void set_name(string name);
  virtual void prepare() = 0;
  virtual void bake();
  virtual void cut();
  virtual void box();
  string toString();
 protected:
  string name_;
  Dough *dough_;
  Sauce *sauce_;
  Cheese *cheese_;
  Clams *clam_;
};

2.2.6 根据批萨接口实现不同皮赛

比如 Cheese Pizza , 同样的 Cheese Pizza 在不同地区的制作流程和配料种类相同,但同类的配料在不同的地区不同,利用原料抽象工厂接口来决定配料的不同。

CheesePizza::CheesePizza(PizzaIngredientFactory *ingredient_factory) :
    ingredient_factory_(ingredient_factory) {
}
void CheesePizza::prepare() {
  cout << "Preparing " << name_ << endl;
  dough_ = ingredient_factory_->createDough();
  sauce_ = ingredient_factory_->createSauce();
  cheese_ = ingredient_factory_->createCheese();
}

2.2.7 整合到一起

比如 New York Pizza Store,通过传入 New York 抽象工厂原料类来创建 New York 自己的批萨。

Pizza* NYPizzaStore::createPizza(string item) {
  Pizza *pizza = NULL;
  PizzaIngredientFactory* ingredientFactory =
      new NYPizzaIngredientFactory();
  if (item == "cheese") {
    pizza = new CheesePizza(ingredientFactory);
    pizza->set_name("New York Style Cheese Pizza");
  } else if (item == "clam") {
    pizza = new ClamPizza(ingredientFactory);
    pizza->set_name("New York Style Clam Pizza");
  }
  return pizza;
}

2.2.8 总的框架

3 总结

3.1 抽象工厂模式(Abstract Factory)结构

3.2 组成

  • 抽象工厂(AbstractFactory)为创建抽象产品类操作定义接口
  • 具体工厂(ConcreteFactory)实现创建具体产品类的操作
  • 抽象产品(AbstractProduct)为一种产品类定义一个接口
  • 具体产品(Concrete Product)
    1. 定义一个将被相应的具体工厂类创建的产品对象
    2. 实现抽象产品(AbstractProduct)的接口
  • 客户(Client)只使用抽象工厂(AbstractFactory)和抽象产品(AbstractProduct)类声明的接口

3.3 应用场景

  • 一个系统要独立于它的产品如何创建,如何组成和如何表示
  • 一个系统需要被一家族产品之一来配置
  • 一家族相关的产品对象要设计成一起使用,你需要强制这个约束
  • 你想提供一类库的产品,而且你想只暴露它们的接口,而不是它们的实现细节。

3.4 缺点

支持新的产品种类较难。扩展抽象工厂来产生新的产品种类并不容易。那是因为抽象工厂接口固定能创建的产品集合。支持新的产品种类要求扩展工厂接口,那这涉及更改抽象工厂类和它的所有子类。

Comments