目录
一.专栏简介
本专栏是我学习《head first》设计模式的笔记。这本书中是用Java语言为基础的,我将用C++语言重写一遍,并且详细讲述其中的设计模式,涉及是什么,为什么,怎么做,自己的心得等等。希望阅读者在读完我的这个专题后,也能在开发中灵活且正确的使用,或者在面对面试官时,能够自信地说自己熟悉常用设计模式。
本章将开始工厂模式的学习。
二.new操作符创建对象
俗话说得好,没有对象那就new一个出来,但我们在new的时候违反了一个设计原则,那就是针对接口编程,而不是针对实现编程。当我们看到"new"操作符时,会想到具体。
把我们的代码绑在具体类上,会使代码更脆弱,更缺乏弹性。
cpp
Duck* duck = new MallardDuck();
我们要用抽象类型来保持代码弹性,但我们不得不创建一个具体类的实例!
当我们有一整群相关的具体类时,经常会这样编码:
cpp
Duck* duck = nullptr;
if(picnic) duck = new MallardDuck();
else if(hunting) duck = new DecoyDuck();
else if(inBathTub) duck = new RubberDuck();
我们有一堆不同的鸭子类直到运行时才会知道需要实例化哪一个。
这里有一些要实例化的具体类,实例化哪一个,要在运行时由一些条件决定。
当你看到这样的代码,你就知道,当有变化或需要扩展时,你将不得不再次打开这份代码,检查需要添加(或删除)的地方。通常,这种代码会分布在系统的各处,使得维护和更新更加困难,而且更容易出错。
那么,怎样把应用中所有实例化具体类的代码拿出来分离,或者封装起来,这样不会影响应用的其他部分?
三.识别变化的方面
在一个披萨店的订单系统中,我们写了这样的代码:
cpp
Pizza orderPizza()
{
Pizza* pizza = new Pizza();
pizza.prepare();
pizza.bake();
pizza.cut();
pizza.box();
return pizza;
}
为了弹性,我们需要Pizza类成为一个抽象类接口,但是我们不能直接实例化抽象类。
但我们需要更多类型的披萨,所以我们添加更多的代码,决定合适的披萨类型,然后再制作披萨:
cpp
Pizza* orderPizza(string type)
{
Pizza* pizza = nullptr;
if (type == "cheese") pizza = new CheesePizza();
else if (type == "greek") pizza = new GreekPizza();
else if (type == "pepperoni") pizza = new PepperoniPizza();
else return nullptr;
pizza->prepare();
pizza->bake();
pizza->cut();
pizza->box();
return pizza;
}
四.压力来自披萨类型的增删查改
当我们要改变披萨的类型时,比如删除一种销量不好的披萨,增加一种大众喜欢的披萨,这时候我们就需要跑到这份代码里修改if语句,也就是说,这份代码的if语句部分没有对修改关闭,但又是经常需要变化的部分。另外,这份代码里的准备,烘焙,切割,装盒操作是固定不变的,这样我们就区分出了代码中变化和不变化的地方。
显然,关于哪个具体类被实例化的代码才是真正搞乱orderPizza()方法的罪魁祸首,它使得代码无法对修改关闭。但现在我们知道哪些会变化,哪些不会变化,可能是时候把它封装起来了。
五.封装对象的创建
我们打算把创建代码移到另一个对象中,该对象只关心创建披萨。
我们称这个对象为:工厂。
工厂处理对象创建的细节。一旦我们有了一个SimplePizzaFactory,orderPizza()方法就变成这个对象的客户。任何时候它需要披萨,就叫披萨工厂做一个。那些orderPizza()方法需要知道希腊披萨或者蛤蜊披萨的日子就一去不复返了。现在orderPizza()方法只关心它得到了一个实现Pizza接口的披萨,这样它就可以调用prepare()、bake()、cut()和box()。
六.建立一个简单的披萨工厂和重做PizzaStore类
我们从工厂本身开始。我们打算定义一个类,这个类封装所有披萨的对象创建。如下所示:
Pizza.h:
cpp
#pragma once
#include <iostream>
#include <string>
using namespace std;
class Pizza
{
public:
void prepare();
void bake();
void cut();
void box();
};
class CheesePizza : public Pizza
{
};
class GreekPizza : public Pizza
{
};
class PepperoniPizza : public Pizza
{
};
class SimplePizzaFactory
{
public:
Pizza* createPizza(string type)
{
Pizza* pizza = nullptr;
if (type == "cheese") pizza = new CheesePizza();
else if (type == "greek") pizza = new GreekPizza();
else if (type == "pepperoni") pizza = new PepperoniPizza();
else return nullptr;
return pizza;
}
};
class PizzaStore
{
public:
PizzaStore(const SimplePizzaFactory& factory);
Pizza* orderPizza(string type);
private:
SimplePizzaFactory factory;
};
Pizza.cpp:
cpp
#include "Pizza.h"
void Pizza::prepare()
{
cout << "准备" << endl;
}
void Pizza::bake()
{
cout << "烘焙" << endl;
}
void Pizza::cut()
{
cout << "切片" << endl;
}
void Pizza::box()
{
cout << "装盒" << endl;
}
PizzaStore::PizzaStore(const SimplePizzaFactory& factory):
factory(factory)
{
}
Pizza* PizzaStore::orderPizza(string type)
{
SimplePizzaFactory spf;
Pizza* pizza = spf.createPizza(type);
pizza->prepare();
pizza->bake();
pizza->cut();
pizza->box();
return pizza;
}
main.cpp:
cpp
#include "Pizza.h"
int main()
{
SimplePizzaFactory factory;
PizzaStore store(factory);
store.orderPizza("cheese");
store.orderPizza("pepperoni");
return 0;
}
运行结果:

这里我们PizzaStore组合了工厂,我们可以动态的改变这个工厂,生产不同风味的披萨。
七.定义简单工厂
我们上面地披萨工厂和披萨商店类就使用了简单工厂这一编程习惯。
简单工厂其实不是一个设计模式,更多是一种编程习惯。一些开发人员的确把这个习惯误认为是工厂模式,不过,下一次碰到这种场合,就可以微秒地展示一下你懂得其中区别,但不要过分炫耀。
下面是基披萨工厂的简单工厂类图:

八.连锁加盟比萨店
披萨店的生意越做越好,我们作为连锁加盟店的经销商,要确保连锁加盟运营的质量,因此希望加盟店都使用我们那些经过时间考验的代码。
但是区域的差异呢?根据加盟店地点以及该地区披萨美食家的口味,每家加盟店可能要提供不同风味的披萨(比如纽约、芝加哥、加州)。

这里就体现了组合的优势。我们把SimplePizzaFactory拿出来,创建三个不同的工厂:NYPizzaFactory、ChicagoPizzaFactory和CaliforniaPizzaFactory,然后,我们只要把PizzaStore和适合工厂组合起来,加盟店的问题就解决了。
我们来看看代码长什么样子~~~代码如下:(这一段是伪代码,不同工厂应该继承自一个基类,然后传基类指针给PizzaStore的构造函数)
cpp
NYPizzaFactory* nyFactory = new NYPizzaFactory();
PizzaStore* nyStore = new PizzaStore(nyFactory );
nyStore.orderPizza("Veggie");
ChicagoPizzaFactory* chicagoFactory = new ChicagoPizzaFactory();
PizzaStore* chicagoStore = new PizzaStore(chicagoFactory );
chicagoStore.orderPizza("Veggie");
这里我们创建做纽约风味披萨的工厂。然后我们创建一个PizzaStore,并给它传递一个纽约工厂的引用。当我们制作披萨时,得到了纽约风味的披萨。芝加哥披萨店也类似。
九.为披萨店而做的框架
上面介绍的简单工厂还要一个单独的工厂类,使用起来不是很方便。有个方法可让披萨制作活动局限于PizzaStore类,同时给予加盟店自由来拥有他们自己的地区风味。
我们打算做的是,把createPizza()方法放回PizzaStore,但这一次作为一个抽象方法,然后为每个区域风味创建一个PizzaStore子类。
首先,我们来看看PizzaStore的变化:
cpp
class PizzaStore
{
public:
Pizza* orderPizza(string type)
{
Pizza* pizza = createPizza(type);
pizza->prepare();
pizza->bake();
pizza->cut();
pizza->box();
return pizza;
}
private:
virtual Pizza* createPizza(string type) = 0;
};
现在createPizza从工厂对象移回PizzaStore,并且是纯虚函数,使得PizzaStore基类成为抽象类接口,咱们面向接口编程,而不是实现。
现在我们有了一个等待着子类的店。我们打算让每个区域类型有一个子类(NYPizzaStore 、ChicagoPizzaStore 、CaliforniaPizzaStore),每个子类将决定披萨由什么组成。我们看看怎么运作。
十.允许子类决定
各个区域之间的差异,在于他们制作披萨的风味,我们打算把所有这些变化放进createPizza()方法。因此,会有一些PizzaStore的具体子类,每个有自己的披萨变体,但都适合PizzaStore框架,依然使用调试好的orderPizza()方法。

代码如下:
Pizza.h:
cpp
#pragma once
#include <iostream>
#include <string>
using namespace std;
class Pizza
{
public:
void prepare();
void bake();
void cut();
void box();
};
class CheesePizza : public Pizza
{
};
class GreekPizza : public Pizza
{
};
class PepperoniPizza : public Pizza
{
};
class NYStyleCheesePizza : public CheesePizza
{
};
class NYStyleGreekPizza : public GreekPizza
{
};
class NYStylePepperoniPizza : public PepperoniPizza
{
};
class ChicagoStyleCheesePizza : public CheesePizza
{
};
class ChicagoStyleGreekPizza : public GreekPizza
{
};
class ChicagoStylePepperoniPizza : public PepperoniPizza
{
};
class PizzaStore
{
public:
Pizza* orderPizza(string type);
protected:
virtual Pizza* createPizza(string type) = 0;
};
class NYStylePizzaStore : public PizzaStore
{
private:
Pizza* createPizza(string type) override;
};
class ChicagoStylePizzaStore : public PizzaStore
{
private:
Pizza* createPizza(string type) override;
};
Pizza.cpp:
cpp
#include "Pizza.h"
void Pizza::prepare()
{
cout << "准备" << endl;
}
void Pizza::bake()
{
cout << "烘焙" << endl;
}
void Pizza::cut()
{
cout << "切片" << endl;
}
void Pizza::box()
{
cout << "装盒" << endl;
}
Pizza* PizzaStore::orderPizza(string type)
{
Pizza* pizza = createPizza(type);
pizza->prepare();
pizza->bake();
pizza->cut();
pizza->box();
return pizza;
}
Pizza* NYStylePizzaStore::createPizza(string type)
{
Pizza* pizza = nullptr;
if (type == "cheese")
{
cout << "NYStyleCheesePizza" << endl;
pizza = new NYStyleCheesePizza();
}
else if (type == "pepperoni")
{
cout << "NYStylePepperoniPizza" << endl;
pizza = new NYStylePepperoniPizza();
}
else if (type == "greek")
{
cout << "NYStyleGreekPizza" << endl;
pizza = new NYStyleGreekPizza();
}
return pizza;
}
Pizza* ChicagoStylePizzaStore::createPizza(string type)
{
Pizza* pizza = nullptr;
if (type == "cheese")
{
cout << "ChicagoStyleCheesePizza" << endl;
pizza = new ChicagoStyleCheesePizza();
}
else if (type == "pepperoni")
{
cout << "ChicagoStylePepperoniPizza" << endl;
pizza = new ChicagoStylePepperoniPizza();
}
else if (type == "greek")
{
cout << "ChicagoStyleGreekPizza" << endl;
pizza = new ChicagoStyleGreekPizza();
}
return pizza;
}
main.cpp:
cpp
#include "Pizza.h"
int main()
{
PizzaStore* store = new NYStylePizzaStore();
store->orderPizza("cheese");
cout << endl;
store->orderPizza("pepperoni");
delete store;
store = nullptr;
cout << endl;
store = new ChicagoStylePizzaStore();
store->orderPizza("cheese");
cout << endl;
store->orderPizza("pepperoni");
return 0;
}
运行结果:

orderPizza()方法对Pizza对象做了许多事情(例如:准备、烘焙、切片、装盒),但由于Pizza是抽象的,orderPizza()不知道涉及了哪一个实际的具体类。换句话说,这就是被解耦了。
书中有一句很绕的话:子类不是真正"决定"(是你通过选择披萨店决定),但它们确实决定了做哪种披萨。

十一.具体化披萨类
实现披萨类之后的代码如下:
Pizza.h:
cpp
#pragma once
#include <iostream>
#include <string>
#include <vector>
using namespace std;
class Pizza
{
public:
void prepare();
void bake();
void cut();
void box();
const string& getName();
protected:
string name;
string dough;
string sauce;
vector<string> toppings;
};
class NYStyleCheesePizza : public Pizza
{
public:
NYStyleCheesePizza();
};
class NYStyleGreekPizza : public Pizza
{
};
class NYStylePepperoniPizza : public Pizza
{
};
class ChicagoStyleCheesePizza : public Pizza
{
public:
ChicagoStyleCheesePizza();
};
class ChicagoStyleGreekPizza : public Pizza
{
};
class ChicagoStylePepperoniPizza : public Pizza
{
};
class PizzaStore
{
public:
Pizza* orderPizza(string type);
protected:
virtual Pizza* createPizza(string type) = 0;
};
class NYStylePizzaStore : public PizzaStore
{
private:
Pizza* createPizza(string type) override;
};
class ChicagoStylePizzaStore : public PizzaStore
{
private:
Pizza* createPizza(string type) override;
};
Pizza.cpp:
cpp
#include "Pizza.h"
void Pizza::prepare()
{
cout << "准备 " + name << endl;
cout << "揉面团......" << endl;
cout << "添加酱汁......" << endl;
cout << "添加馅料:";
for (const auto& e : toppings)
{
cout << " " << e;
}
cout << endl;
}
void Pizza::bake()
{
cout << "350°烘焙25分钟" << endl;
}
void Pizza::cut()
{
cout << "将披萨按照对角线切割" << endl;
}
void Pizza::box()
{
cout << "将披萨装盒" << endl;
}
const string& Pizza::getName()
{
return name;
}
Pizza* PizzaStore::orderPizza(string type)
{
Pizza* pizza = createPizza(type);
pizza->prepare();
pizza->bake();
pizza->cut();
pizza->box();
return pizza;
}
Pizza* NYStylePizzaStore::createPizza(string type)
{
Pizza* pizza = nullptr;
if (type == "cheese")
{
cout << "NYStyleCheesePizza" << endl;
pizza = new NYStyleCheesePizza();
}
else if (type == "pepperoni")
{
cout << "NYStylePepperoniPizza" << endl;
pizza = new NYStylePepperoniPizza();
}
else if (type == "greek")
{
cout << "NYStyleGreekPizza" << endl;
pizza = new NYStyleGreekPizza();
}
return pizza;
}
Pizza* ChicagoStylePizzaStore::createPizza(string type)
{
Pizza* pizza = nullptr;
if (type == "cheese")
{
cout << "ChicagoStyleCheesePizza" << endl;
pizza = new ChicagoStyleCheesePizza();
}
else if (type == "pepperoni")
{
cout << "ChicagoStylePepperoniPizza" << endl;
pizza = new ChicagoStylePepperoniPizza();
}
else if (type == "greek")
{
cout << "ChicagoStyleGreekPizza" << endl;
pizza = new ChicagoStyleGreekPizza();
}
return pizza;
}
NYStyleCheesePizza::NYStyleCheesePizza()
{
name = "纽约风味酱料芝士比萨";
dough = "薄底面团";
sauce = "意大利番茄酱";
toppings.push_back("磨碎的雷吉亚诺奶酪");
}
ChicagoStyleCheesePizza::ChicagoStyleCheesePizza()
{
name = "芝加哥风格深盘奶酪披萨";
dough = "特厚披萨饼底面团";
sauce = "李子番茄酱";
toppings.push_back("碎马苏里拉奶酪");
}
main.cpp:
cpp
#include "Pizza.h"
int main()
{
PizzaStore* store = new NYStylePizzaStore();
store->orderPizza("cheese");
delete store;
store = nullptr;
cout << endl;
store = new ChicagoStylePizzaStore();
store->orderPizza("cheese");
return 0;
}
运行结果如下:

两个披萨都准备好了,馅料已加,已烘焙、切片、装盒,超类不必知道细节,子类只是通过实例化正确的披萨来处理所有这些。
十二.会见工厂方法模式的最后时刻
所有工厂模式都封装对象的创建。工厂方法模式通过让子类决定创建哪个对象,来封装对象的创建。我们来看看这些类图,了解这个模式中有哪些玩家:

十三.平行视角看创建者和产品
对于每一个具体创建者,通常有一整套产品要创建。芝加哥披萨创建者创建不同类型的芝加哥风味披萨,纽约披萨创建者创建不同类型的纽约风味披萨等。事实上,我们可以看到创建者类及其相应的产品类集平行的层次。
我们来看看两个平行类层次以及它们如何相关:

十四.定义工厂方法模式
工厂方法模式的官方定义:
工厂方法模式定义了一个创建对象的接口,但由子类决定要实例化哪个类。工厂方法让类把实例化推迟到子类。
我们会经常听到开发人员说,"工厂方法模式让子类决定实例化哪个类"。因为Creator类不知道将被创建的实际产品,我们说"决定",不是因为该模式允许子类本身决定,该决定实际上指用哪个子类创建产品。

十五.总结
本篇文章主要介绍了简单工厂和工厂方法模式,下篇会介绍抽象方法模式,依赖倒置原则等等。