04. 简单工厂模式
一、简单工厂模式概述
简单工厂模式(Simple Factory Pattern)并不属于 GoF 23个经典设计模式,但通常将它作为学习其他工厂模式的基础,它的设计思想很简单。简单工厂模式 定义一个 工厂类,它可以 根据参数的不同返回不同类的实例,被创建的实例通常都具有 共同的父类。因为在简单工厂模式中用于创建实例的方法是 静态方法(static),因此简单工厂模式又被称为 静态工厂方法模式(Static Factory Method),它属于类创建型模式。
简单工厂模式先将需要创建的各种不同对象的相关代码封装到不同的类中,这些类称为 具体产品类,而将它们公共的代码进行抽象和提取后封装在一个 抽象产品类 中,每一个具体产品类都是抽象产品类的子类。然后提供一个 工厂类 用于创建各种产品,在工厂类中提供一个创建产品的工厂方法,该方法可以 根据所传入的参数不同创建不同的具体产品对象。客户端只需调用工厂类的工厂方法并传入相应的参数即可得到一个产品对象。
简单工厂模式的主要角色如下:
- 工厂角色(Factory):即工厂类,它是简单工厂模式的核心,负责实现创建所有产品实例的内部逻辑。工厂类可以被外界直接调用,创建所需的产品对象。在工厂类中提供了静态的工厂方法 factoryMethod(),它的返回类型为抽象产品类型 Product。
- 抽象产品角色(Product):它是工厂类所创建的所有对象的父类,封装了各种产品对象的公有方法。抽象产品的引入将提高系统的灵活性,使得在工厂类中只需定义一个通用的工厂方法,因为所有创建的具体产品对象都是其子类对象。
- 具体产品角色(ConcreteProduct):它是简单工厂模式的创建目标,所有被创建的对象都充当这个角色的某个具体类的实例。每个具体产品角色都继承了抽象产品角色,需要实现在抽象产品中声明的抽象方法。
二、C++实现简单工厂模式
将所有产品类公共的代码移至抽象产品类,并在抽象产品类中声明一些抽象方法,以供不同的具体产品类来实现。
// 抽象披萨类
class Pizza
{
private:
std::string name;
public:
Pizza(void);
Pizza(std::string name);
virtual void prepare(void) = 0; // 准备原材料,这里不同披萨准备原材料不同
void bake(void); // 烘烤,这里不同披萨烘烤方式相同
void cut(void); // 切块,这里不同披萨切块方式相同
void box(void); // 装盒,这里不同披萨装盒方式相同
std::string getName();
void setName(std::string name);
};
Pizza::Pizza(void) {}
Pizza::Pizza(std::string name) : name(name) {}
void Pizza::bake(void)
{
std::cout << name + "烘烤" << std::endl;
}
void Pizza::cut(void)
{
std::cout << name + "切块" << std::endl;
}
void Pizza::box(void)
{
std::cout << name + "装盒" << std::endl;
}
std::string Pizza::getName(void)
{
return name;
}
void Pizza::setName(std::string name)
{
this->name = name;
}
在具体产品类中实现了抽象产品类中声明的抽象业务方法,不同的具体产品类可以提供不同的实现。
// 奶酪披萨
class CheesePizza : public Pizza
{
public:
using Pizza::Pizza;
void prepare(void) override;
};
void CheesePizza::prepare(void)
{
std::cout << "准备" + getName() + "的原材料" << std::endl;
std::cout << "准备奶酪" << std::endl;
}
// 水果披萨
class FruitPizza : public Pizza
{
public:
using Pizza::Pizza;
void prepare(void) override;
};
void FruitPizza::prepare(void)
{
std::cout << "准备" + getName() + "的原材料" << std::endl;
std::cout << "准备水果" << std::endl;
}
在简单工厂模式中,工厂类提供了一个静态工厂方法供客户端使用,根据所传入的参数不同可以创建不同的产品对象。
// 披萨工厂,根据顾客的订单,来生产不同的披萨
class PizzaFactory
{
public:
Pizza * ceratePizza(std::string pizza);
};
Pizza * PizzaFactory::ceratePizza(std::string pizza_name)
{
Pizza * pizza = nullptr;
if (pizza_name == "奶酪披萨")
{
pizza = new CheesePizza();
}
else if (pizza_name == "水果披萨")
{
pizza = new FruitPizza();
}
return pizza;
}
在客户端代码中,只需关心工厂类即可。简单工厂根据参不同的参数创建不同的具体产品的实例。
// 披萨店,根据顾客的订单,来选择不同的披萨
class PizzaStore
{
private:
PizzaFactory * pizzaFactory;
public:
Pizza * order(std::string pizza_name); // 顾客订购披萨
void setPizzaFactory(PizzaFactory * pizzaFactory);
};
void PizzaStore::setPizzaFactory(PizzaFactory * pizzaFactory)
{
this->pizzaFactory = pizzaFactory;
}
Pizza * PizzaStore::order(std::string pizza_name)
{
Pizza * pizza = pizzaFactory->ceratePizza(pizza_name); // 根据顾客的订单,来选择不同的披萨
pizza->prepare();
pizza->bake();
pizza->cut();
pizza->box();
return pizza;
}
main() 函数:
#include <iostream>
int main(void)
{
PizzaStore pizzaStore;
// 顾客订购奶酪披萨
PizzaFactory * pizzaFactory = new PizzaFactory();
pizzaStore.setPizzaFactory(pizzaFactory);
Pizza * cheesePizza = pizzaStore.order("奶酪披萨");
std::cout << std::endl;
// 顾客订购水果披萨
Pizza * fruitPizza = pizzaStore.order("水果披萨");
delete pizzaFactory;
delete cheesePizza;
delete fruitPizza;
return 0;
}
三、简单工厂模式的总结
3.1、简单工厂模式的优点
- 工厂类包含必要的判断逻辑,可以决定在什么时候创建哪一个产品类的实例。客户端可以免除直接创建产品对象的职责,而仅仅 “消费” 产品。简单工厂模式实现了对象创建和使用的分离。
- 客户端无须知道所创建的具体产品类的类名,只需要知道具体产品类所对应的参数即可。对于一些复杂的类名,通过简单工厂模式可以在一定程度减少使用者的记忆量。
- 通过引入配置文件,可以在不修改任何客户端代码的情况下更换和增加新的具体产品类,在一定程度上提高了系统的灵活性。
3.2、简单工厂模式的缺点
- 由于工厂类集中了所有产品的创建逻辑,职责过重,一旦不能正常工作,整个系统都要受到影响。
- 使用简单工厂模式势必会增加系统中类的个数(引入了新的工厂类),增加了系统的复杂度和理解难度。
- 系统扩展困难。一旦添加新产品就不得不修改工厂逻辑,在产品类型较多时,有可能造成工厂逻辑过于复杂,不利于系统的扩展和维护。
- 简单工厂模式由于使用了静态工厂方法,造成工厂角色无法形成基于继承的等级结构。
3.3、简单工厂模式的适用场景
- 工厂类负责创建的对象比较少。由于创建的对象较少,不会造成工厂方法中的业务逻辑太过复杂。
- 客户端只知道传入工厂类的参数,对于如何创建对象并不关心。