一.为什么需要工厂模式
当我们在拥有了大量类的时候,一旦我们需要具体对象,就需要手动控制new出不同的对象。我们可以针对这一步骤抽象化,创建一个根据要求返回我们需要的具体对象的工厂。这样就能通过统一的方式获得不同的对象
二.不用工厂模式
假设我们拥有一个披萨店,我们需要根据客户不同的订单提供不同的披萨。首先不使用工厂模式,我们就能得到这样的代码
cpp
#include <iostream>
#include <string>
class Pizza {
public:
virtual void prepare() = 0;
virtual void bake() = 0;
virtual void cut() = 0;
virtual void box() = 0;
};
//不同种类的披萨
class CheesePizza: public Pizza {
void prepare()
{
std::cout << "准备芝士" << std::endl;
};
void bake()
{
std::cout << "烤芝士" << std::endl;
};
void cut()
{
std::cout << "切芝士" << std::endl;
};
void box()
{
std::cout << "装盒" << std::endl;
};
};
class GreekPizza: public Pizza {
void prepare()
{
std::cout << "。。。" << std::endl;
};
void bake()
{
std::cout << "。。。" << std::endl;
};
void cut()
{
std::cout << "。。。" << std::endl;
};
void box()
{
std::cout << "。。。" << std::endl;
};
};
class PepperoniPizza: public Pizza {
void prepare()
{
std::cout << "。。。" << std::endl;
};
void bake()
{
std::cout << "。。。" << std::endl;
};
void cut()
{
std::cout << "。。。" << std::endl;
};
void box()
{
std::cout << "。。。" << std::endl;
};
};
std::unique_ptr<Pizza> orderPizza(std::string type)
{
std::unique_ptr<Pizza> pizza = nullptr;
if (type == "cheese")
{
pizza = std::make_unique<CheesePizza>();
}
else if (type == "greek")
{
pizza = std::make_unique<GreekPizza>();
}
else if (type == "pepperoni")
{
pizza = std::make_unique<PepperoniPizza>();
}
pizza->prepare();
pizza->bake();
pizza->cut();
pizza->box();
return pizza;
};
三.使用简单工厂模式
能看到假若我们想要对具体类型的披萨进行调整是很困难的。我们将工厂引入,单独用一个函数创建对象。就可以得到这样的代码。通过统一的createPizza函数获得不同的披萨对象
cpp
#include "Pizza.hpp"
#include <string>
class SimplePizzaFactory
{
public:
std::unique_ptr<Pizza> createPizza(const std::string& type)
{
std::unique_ptr<Pizza> pizza = nullptr;
if (type == "cheese")
{
pizza = std::make_unique<CheesePizza>();
}
else if (type == "pepperoni")
{
pizza = std::make_unique<PepperoniPizza>();
}
else if (type == "clam")
{
pizza = std::make_unique<ClamPizza>();
}
else if (type == "veggie")
{
pizza = std::make_unique<VeggiePizza>();
}
return pizza;
}
};
这样结合工厂模式就可以得到如下的披萨店的代码。
cpp
#include "SimplePizzaFactory.hpp"
#include "Pizza.hpp"
#include <string>
class PizzaStore
{
SimplePizzaFactory factory;
public:
PizzaStore(SimplePizzaFactory factory)
{
this->factory = factory;
}
std::unique_ptr<Pizza> orderPizza(std::string type)
{
std::unique_ptr<Pizza> pizza = nullptr;
pizza = factory.createPizza(type);
if (pizza != nullptr)
{
pizza->prepare();
pizza->bake();
pizza->cut();
pizza->box();
}
else
{
std::cout << "不是可提供的披萨" << std::endl;
}
return pizza;
}
};
这样就可以通过字符串自动判断并生成对应的对象。也有利于我们增加新的披萨种类。但是对于更改已有披萨的设计仍然不是很方便。
四.工厂模式
我们想让每种不同的披萨对象拥有更多的弹性。我们可以将这一弹性功能下放到PizzaStore实现。也就是让子类运行时根据输入决定做什么披萨,怎么做披萨。我们需要提供多种不同类型的PizzaStore,并根据不同的方法创建不同的披萨类型。
首先提供披萨店的接口和披萨的接口
cpp
#include "Pizza.hpp"
#include <string>
class PizzaStore
{
public:
std::unique_ptr<Pizza> orderPizza(std::string type);
virtual std::unique_ptr<Pizza> makePizza(std::string type) = 0;
};
cpp
class Pizza
{
public:
std::string name;
std::string dough;
std::string sauce;
std::vector<std::string> toppings;
const std::string& getName() const
{
return this->name;
}
virtual void prepare() const
{
std::cout << "准备 " + getName() << std::endl;
std::cout << "准备面团..." << std::endl;
std::cout << "添加酱汁..." << std::endl;
std::cout << "添加配料: " << std::endl;
for (auto& topping : this->toppings)
{
std::cout << " " + topping + "\n";
}
}
virtual void bake() const
{
std::cout << "350 度 烤 25 分钟 "<< std::endl;
}
virtual void cut() const
{
std::cout << "切分披萨"<< std::endl;
}
virtual void box() const
{
std::cout << "披萨装盒" << std::endl;
}
virtual ~Pizza() = default;
};
有了披萨店的接口和披萨的接口,我们就能创建出不同种类的披萨商店贩卖不同种类的披萨。
首先创建出不同类型的披萨。并将其装载在披萨店内。
cpp
#include "Pizza.hpp"
class NYStylePepperoniPizza: public Pizza
{
public:
NYStylePepperoniPizza()
{
name = "NY style pepperoni pizza";
dough = "。。。面";
sauce = "。。。酱";
toppings.push_back("。。。e");
toppings.push_back("。。。");
toppings.push_back("。。。");
toppings.push_back("。。。");
toppings.push_back("。。。");
toppings.push_back("。。。");
}
};
cpp
#include "Pizza.hpp"
#include "PizzaStore.hpp"
class NYPizzaStore: public PizzaStore
{
public:
std::unique_ptr<Pizza> makePizza(std::string type)
{
std::unique_ptr<Pizza> pizza = nullptr;
if (type == "cheese")
{
pizza = std::make_unique<NYStyleCheesePizza>();
}
else if (type == "pepperoni")
{
pizza = std::make_unique<NYStylePepperoniPizza>();
}
return pizza;
}
};
这样披萨店的工厂会拥有更好的弹性,能更利于增加新的披萨,由于每个商店都拥有一个特有的披萨类,也利于披萨类本身的更改。
披萨本身和披萨店本身是平行的关系,披萨是产品类,披萨商店是创建者
工厂模式定义:定义了一个创建对象的接口,但让子类决定实例化哪个类。
本质就是工厂接口创建出工厂实例,调用产品接口实现出产品实例。
五.工厂模式应有的对象依赖关系
依赖抽象不依赖具体类,应该让依赖倒置。高层组件的类例如(pizzastore)依赖具体实现类例如(具体pizza类)。
六.抽象工厂模式
对于上面的工厂模式的披萨而言,原料都是固定好的,不利于修改,而抽象工厂模式利于提供大量家族相关对象,就比如上面的披萨原料。我们可以通过抽象工厂来改造披萨的原料。首先获得我们新的pizza虚接口,跟工厂模式没有什么变化。
cpp
class Pizza
{
public:
std::string name;
Dough dough;
Sauce sauce;
std::vector<std::string> toppings;
const std::string& getName() const
{
return this->name;
}
virtual void prepare() const //抽象工厂的重点是这个函数
{
std::cout << "准备 " + getName() << std::endl;
std::cout << "准备面团..." << std::endl;
std::cout << "添加酱汁..." << std::endl;
std::cout << "添加配料: " << std::endl;
for (auto& topping : this->toppings)
{
std::cout << " " + topping + "\n";
}
}
virtual void bake() const
{
std::cout << "350 度 烤 25 分钟 "<< std::endl;
}
virtual void cut() const
{
std::cout << "切分披萨"<< std::endl;
}
virtual void box() const
{
std::cout << "披萨装盒" << std::endl;
}
virtual ~Pizza() = default;
};
主要的变化是我们将用到工厂内的组件也就是工厂家族,具体的实现过程抽象工厂实现。首先看一下批量获取相关原料的工厂接口。
cpp
class PizzaIngredientFactory
{
public:
virtual Dough CreatDough() = 0;
virtual Cheese CreatCheese() = 0;
.
.
.
};
我们可以根据不同的披萨商店,形成不同的具体原材料工厂实例
cpp
class NYStylePepperoniPizza: public PizzaIngredientFactory
{
Dough createDough()
{
return new ThinCrustDough();
}
Cheese createCheese()
{
return new MarinaraCheese();
}
.
.
.
}
我们用一个例子来实现。我们让披萨制作阶段运行时调整,通过原材料工厂获取不同的原材料
cpp
#include "Pizza.hpp"
class NYStylePepperoniPizza: public Pizza
{
PizzaIngredientFactory _ingFact;
public:
NYStylePepperoniPizza(PizzaIngredientFactory ingFact) //通过工厂元素获取原料
{
this._ingFact = ingFact;
}
void prepare() //通过原料工厂批量获取对象
{
dough = _ingFact.createDough();
sauce = _ingFact.createsauce();
cheese = _ingFact.createcheese();
}
};
最后我们需要更改披萨商店调用原材料工厂
cpp
#include "Pizza.hpp"
#include "PizzaStore.hpp"
class NYPizzaStore: public PizzaStore
{
public:
std::unique_ptr<Pizza> makePizza(std::string type)
{
std::unique_ptr<Pizza> pizza = nullptr;
PizzaIngredientFactory idFact = new NYPizzaIngredientFactory (); //拿到原材料工厂
if (type == "cheese")
{
pizza = NYStyleCheesePizza(idFact); //通过不同的工厂加工生成不同的披萨
}
else if (type == "pepperoni")
{
pizza = NYStylePepperoniPizza(idFact);
}
return pizza;
}
};
这样就实现了不同工厂通过抽象工厂获得批量不同的元素,生成对应的披萨。
总结:通过引进一个抽象工厂提供了为披萨家族创建家族。此工厂提供接口,我们通过这个接口获取对象。我们可以通过更换工厂获得不同的行为。
抽象工厂提供一个接口来创建相关或依赖对象的家族,而不需要提供具体类
七.对比工厂模式和抽象工厂
抽象工厂通过对象组合创建对象,通过提供一个抽象接口,规定如何生产相关产品,工厂方法通过子类创建对象。
抽象工厂用于提供大批量相关产品家族。
工厂用于从具体的实例化解耦。
抽象工厂往往会在具体实例化使用工厂模式
抽象工厂缺点:添加新产品意味着改变接口。
总结:
抽象工厂提供一个接口,用于创建相关依赖对象的家族,而不必指定它们的具体类。依靠对象组合实现,子类创建在工厂接口暴露的方法中实现。抽象工厂的意图是创建相关对象家族,不必依赖具体类
工厂方法,定义一个创建对象的接口,但让子类决定哪个类实例化,工厂方法让一个类延迟实例化到子类。工厂方法依赖继承,对象创建被委托给子类,子类实现工厂方法来创建对象。工厂方法的意图是允许类延迟实例化