工厂模式(Factory)
定义(Definition):为一类派生类创建实例的实用类
抽象工厂(Abstract Factory)
定义(Definition):为一类派生类创建实例的实用类。也可以创建一个工厂的实例。
工厂模式在需要创建许多不同类型的对象的情况下非常有用,这些对象都是从公共基类派生的。工厂模式定义了创建对象的方法,子类可以覆盖该方法来指定创建的派生对象。因此,在程序运行期间,工厂模式可以传入期望对象的描述(如:用户输入的字符串),然后返回一个指向期望对象的指针。当基类接口设计的很好时,这个模式会很有效,因为不需要转换返回的对象。
问题(Problem)
我们想根据一些配置参数或者应用参数决定程序运行时创建的对象。当我们编写代码时,我们并不知道要实例化什么类。
解决方法(Solution)
定义创建对象的接口,然后让子类决定实例化什么类。工厂模式允许类将实例化推迟到子类。
下面的例子中,工厂模式被用来在运行时创建笔记本或者台式电脑。
首先,定义一个抽象基类(接口)Computer以及派生类:Laptop和Desktop。
class Computer
{
public:
virtual void Run() = 0;
virtual void Stop() = 0;
virtual ~Computer() {}; /* without this, you do not call Laptop or Desktop destructor in this example! */
};
class Laptop: public Computer
{
public:
void Run() override {mHibernating = false;};
void Stop() override {mHibernating = true;};
virtual ~Laptop() {}; /* because we have virtual functions, we need virtual destructor */
private:
bool mHibernating; // Whether or not the machine is hibernating
};
class Desktop: public Computer
{
public:
void Run() override {mOn = true;};
void Stop() override {mOn = false;};
virtual ~Desktop() {};
private:
bool mOn; // Whether or not the machine has been turned on
};
ComputerFactory类根据传参description,返回Computer类的指针
class ComputerFactory
{
public:
static Computer *NewComputer(const std::string &description)
{
if(description == "laptop")
return new Laptop;
if(description == "desktop")
return new Desktop;
return nullptr;
}
};
让我们分析下这个设计模式的好处。首先,编译的好处。如果我们把Computer接口类和ComputerFactory工厂类放在单独的头文件中,把NewComputer()函数和派生类放到单独的实现文件中。这个含有NewComputer()函数的文件是唯一依赖派生类的。因此,如果Computer的派生类有改动或者添加了新的派生类,这个含有NewComputer()函数的文件是唯一需要重新编译的文件。任何使用这个工厂模式的人只要保证这个接口在整个软件中的一致性。
然后,如果需要添加一个派生类,原本的调用工厂模式生成对象的代码不需要改动。要产生新的派生类的对象,也只需要简单的传一个新的字符串给工厂,工厂就会返回新的派生类对象。
另一个例子(C++17标准):
#include <stdexcept>
#include <iostream>
#include <memory>
using namespace std;
class Pizza {
public:
virtual int getPrice() const = 0;
virtual ~Pizza() {}; /* without this, no destructor for derived Pizza's will be called. */
};
class HamAndMushroomPizza : public Pizza {
public:
virtual int getPrice() const { return 850; };
virtual ~HamAndMushroomPizza() {};
};
class DeluxePizza : public Pizza {
public:
virtual int getPrice() const { return 1050; };
virtual ~DeluxePizza() {};
};
class HawaiianPizza : public Pizza {
public:
virtual int getPrice() const { return 1150; };
virtual ~HawaiianPizza() {};
};
class PizzaFactory {
public:
enum PizzaType {
HamMushroom,
Deluxe,
Hawaiian
};
static unique_ptr<Pizza> createPizza(PizzaType pizzaType) {
switch (pizzaType) {
case HamMushroom: return make_unique<HamAndMushroomPizza>();
case Deluxe: return make_unique<DeluxePizza>();
case Hawaiian: return make_unique<HawaiianPizza>();
}
throw "invalid pizza type.";
}
};
/*
* Create all available pizzas and print their prices
*/
void pizza_information(PizzaFactory::PizzaType pizzatype)
{
unique_ptr<Pizza> pizza = PizzaFactory::createPizza(pizzatype);
cout << "Price of " << pizzatype << " is " << pizza->getPrice() << std::endl;
}
int main()
{
pizza_information(PizzaFactory::HamMushroom);
pizza_information(PizzaFactory::Deluxe);
pizza_information(PizzaFactory::Hawaiian);
}