引言
在OO程序设计中一种很常见的情景是要根据输入来选择具体要实例化的类,比如说在一个公交卡系统里面,分为学生卡和教师卡,经常会做这样的一个判定。
# class Card;
# class StudentCard : public Card
# class TeacherCard : public Card
Card *card;
if(input == 'student'){
card = new StudentCard();
}
else if(input == 'teacher'){
card = new TeacherCard();
}
这是很正常的想法,但是一旦增加了新的卡的种类或有了其他的变化,都必须更改以上类似的代码。
这只是其中一个例子,再看看Head First Design Pattern中给出的Pizza例子。Head First中要求在一家PizzaStore中根据不同的选择,实例化不同的具体Pizza类。
# factory.h
class Pizza {
public:
virtual std::string getName();
virtual void prepare();
virtual void bake();
virtual void cut();
virtual void box();
protected:
std::string name = "Pizza";
};
class ClamPizza : public Pizza {
public:
ClamPizza() : Pizza() { }
protected:
std::string name = "ClamPizza";
};
class VeggiePizza : public Pizza {
public:
VeggiePizza() : Pizza() { }
protected:
std::string name = "VeggiePizza";
};
class PizzaStore {
public:
Pizza* orderPizza(std::string choice);
};
# factory.cpp
std::string Pizza::getName() {
return name;
}
void Pizza::prepare() {
std::cout << getName() << " is preparing." << std::endl;
}
void Pizza::bake() {
std::cout << getName() << " is baking." << std::endl;
}
void Pizza::cut() {
std::cout << getName() << " is cutting." << std::endl;
}
void Pizza::box() {
std::cout << getName() << " is boxing." << std::endl;
}
Pizza* PizzaStore::orderPizza(std::string choice) {
Pizza* pizza;
if(choice == "clam"){
pizza = new ClamPizza();
}
else if(choice == "veggie"){
pizza = new VeggiePizza();
}
else{
pizza = new Pizza();
}
pizza->prepare();
pizza->bake();
pizza->cut();
pizza->box();
return pizza;
}
一旦Pizza的类型多了起来,我们就需要不断的修改orderPizza方法。
简单工厂
找出应用中可能变化支持,把它们独立出来,不要和那些不需要变化的的代码混在一起。 –《Head First Design Pattern》
把实例化具体类的代码从orderPizza中分离出来,并分装到一个专门生产Pizza的工厂中。
# factory.h
class SimplePizzaFactory {
public:
Pizza* createPizza(std::string choice);
};
class PizzaStore {
public:
PizzaStore(SimplePizzaFactory *fa);
Pizza* orderPizza(std::string choice);
private:
SimplePizzaFactory *factory;
};
# factory.cpp
Pizza* PizzaStore::orderPizza(std::string choice) {
Pizza* pizza;
pizza = factory->createPizza(choice);
pizza->prepare();
pizza->bake();
pizza->cut();
pizza->box();
return pizza;
}
Pizza* SimplePizzaFactory::createPizza(std::string choice) {
Pizza* pizza;
if(choice == "clam"){
pizza = new ClamPizza();
}
else if(choice == "veggie"){
pizza = new VeggiePizza();
}
else{
pizza = new Pizza();
}
return pizza;
}
看起来并没有太大的变化,只是简单地将具体化类的代码放在了SimplePizzaFactory中,但是当代码中如果存在需要Pizza对象的地方,SimplePizzaFactory就能达到复用的效果了。
需要注意的是,简单工厂更多的是一种优秀的编程习惯,而并非一种设计模式。
工厂方法
接下来考虑下一种情景,如果想要再多开几家不同地区的PizzaStore,要求它们仅仅是制造的Pizza的风味不一样,并且必须遵守orderPizza的步骤。此时该怎么办呢?
- 要制造不同风味的Pizza,则要求以一个抽象的工厂基类实现不同的工厂,每个工厂实现createPizza方法。
- 多开几家不同地区的PizzaStore,则要求以一个抽象的商店基类实现不同的具体商店。
- 结论:通过PizzaStore的子类的实例化来决定Pizza风味的定制。(因为地区决定了风味,即在PizzaStore的子类里面可以确定要求实例化的工厂)
这时候,PizzaStore就相当于工厂了,将createPizza定义成静态的抽象方法,就能通过子类的实例化达到不同风味的Pizza定制。
通过对PizzaStore的orderPizza加上final实现对子类制作Pizza的控制。
# factory.h
class PizzaStore {
public:
Pizza* orderPizza(std::string choice);
protected:
virtual Pizza* createPizza(std::string choice) = 0;
};
class NYPizzaStore : public PizzaStore {
public:
Pizza* createPizza(std::string choice) override;
};
# factory.cpp
Pizza* PizzaStore::orderPizza(std::string choice) {
Pizza* pizza;
pizza = createPizza(choice);
pizza->prepare();
pizza->bake();
pizza->cut();
pizza->box();
return pizza;
}
Pizza* NYPizzaStore::createPizza(std::string choice) {
Pizza* pizza;
if(choice == "clam"){
pizza = new ClamPizza();
}
else if(choice == "veggie"){
pizza = new VeggiePizza();
}
else{
pizza = new Pizza();
}
return pizza;
}
工厂方法定义了一个创建对象的接口,但由子类决定要实例化的类是哪一个。工厂方法让类把实例化推迟到子类。 – 《Head First Design Pattern》
注意: 不一定要通过子类的实例化来建造工厂,可以设定默认的工厂方法(解除抽象)。
(依赖倒置)依赖抽象,不要依赖具体类。– 《Head First Design Pattern》
- 正常来说,我们考虑的思路是PizzaStore生产Pizza,而不同的Pizza就是具体类,也就是说顶端就是PizzaStore,底层是Pizza,PizzaStore依赖着Pizza。
- 这时候根据依赖倒置的原则我们可以从Pizza开始考虑,Pizza要根据一个抽象来集合具体类,而PizzaStore也要根据一个抽象来决定Pizza的具体类。
指导方针(提醒不破坏依赖倒置原则)
变量不可以具有具体类的引用。
不要让类派生自具体类。
不要覆盖基类中已实现的方法。
抽象工厂
接下来继续考虑当一个工厂需要同时给客户提供多种产品的情景。
以上面的Pizza原料为例,每一间PizzaStore需要一间不一样的原料工厂来准备Pizza的原料,而Pizza的原料是有多种的,也就是说原料工厂必须准备一组产品(原料)给Pizza(客户)。
我们先重新设置一下具体的Pizza类。
# factory.h
/*
* Abstract Ingredient
*/
class Dough {
public:
std::string getName();
protected:
std::string name = "Dough";
};
class Sauce {
public:
std::string getName();
protected:
std::string name = "Sauce";
};
class Veggies {
public:
std::string getName();
protected:
std::string name = "Veggies";
};
class Cheese {
public:
std::string getName();
protected:
std::string name = "cheese";
};
/*
* Abstract Pizza
*/
class Pizza {
public:
virtual std::string getName();
virtual void prepare() = 0;
virtual void bake();
virtual void cut();
virtual void box();
protected:
std::string name = "Pizza";
Dough* dough;
Sauce* sauce;
Veggies* veggies;
Cheese* cheese;
};
接下来看高层和低层都需要依赖的抽象PizzaIngredientFactory:
class PizzaIngredientFactory {
public:
virtual Dough* createDough() = 0;
virtual Sauce* createSauce() = 0;
virtual Veggies* createVeggies() = 0;
virtual Cheese* createCheese() = 0;
};
具体Pizza类的实现,在Prepare方法中准备原料,以及具体工厂类的实现。
# factory.h
class CheesePizza : public Pizza {
public:
CheesePizza(PizzaIngredientFactory *fa) : factory(fa) { }
void prepare() override ;
private:
PizzaIngredientFactory *factory;
};
class NYPizzaIngredientFactory : public PizzaIngredientFactory {
public:
Dough* createDough() override ;
Sauce* createSauce() override ;
Veggies* createVeggies() override ;
Cheese* createCheese() override ;
};
# factory.cpp
void CheesePizza::prepare() {
dough = factory->createDough();
sauce = factory->createSauce();
veggies = factory->createVeggies();
cheese = factory->createCheese();
}
Dough* NYPizzaIngredientFactory::createDough() {
return new ConcreteDough();
}
Sauce* NYPizzaIngredientFactory::createSauce() {
return new ConcreteSauce();
}
Veggies* NYPizzaIngredientFactory::createVeggies() {
return new ConcreteVeggies();
}
Cheese* NYPizzaIngredientFactory::createCheese() {
return new ConcreteCheese();
}
抽象工厂与工厂方法很大的一点不同就是抽象工厂在高层组件是通过对象组合来实现工厂(在Pizza具体类中有一个factory指针),而工厂方法在高层组件是通过对象继承来实现工厂。
但是,抽象工厂包含了工厂方法,比如上述的createDough等方法,都是通过在工厂子类中再实例化。
最后,只要修改NYPizzaStore创造Pizza的方法就可以了:
Pizza* NYPizzaStore::createPizza(std::string choice) {
Pizza* pizza;
NYPizzaIngredientFactory *fa = new NYPizzaIngredientFactory();
if(choice == "cheese"){
pizza = new CheesePizza(fa);
}
return pizza;
}
抽象工厂模式提供一个接口,用于创建相关或依赖对象的家族,而不需要明确指定具体类。 –《Head First Design Pattern》