1 实例
继续扩展模式四中的批萨店实例,加入批萨配料对象。不同的地区的批萨店有一组共同的产品(如生面,酱汁,芝士等等),但是根据不同地区的口味有不同的实现。整个系统变得有点复杂了,
- 有不同的批萨原料,同样的批萨原料在不同的地区有不同的作法;
- 每个店有不同种类的批萨(即各种原料搭配不同);
- 整个批萨连锁在不同地区有不同的店。
如何清晰而易维护的设计整个批萨连锁店系统呢?
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)
- 定义一个将被相应的具体工厂类创建的产品对象
- 实现抽象产品(AbstractProduct)的接口
- 客户(Client)只使用抽象工厂(AbstractFactory)和抽象产品(AbstractProduct)类声明的接口
3.3 应用场景
- 一个系统要独立于它的产品如何创建,如何组成和如何表示
- 一个系统需要被一家族产品之一来配置
- 一家族相关的产品对象要设计成一起使用,你需要强制这个约束
- 你想提供一类库的产品,而且你想只暴露它们的接口,而不是它们的实现细节。
3.4 缺点
支持新的产品种类较难。扩展抽象工厂来产生新的产品种类并不容易。那是因为抽象工厂接口固定能创建的产品集合。支持新的产品种类要求扩展工厂接口,那这涉及更改抽象工厂类和它的所有子类。