一.意图
工厂方法是一种创建设计模式,提供创建超类对象的接口,同时允许子类更改将要创建的对象类型。
通过"对象创建"模式绕开new,来避免对象创建(new)过程中所导致的紧耦合(依赖具体类),从而支持对象创建的稳定。它是接口抽象之后的第一步工作。
在软件系统中,经常面临着创建对象的工作;由于需求的变化,需要创建的对象的具体类型经常变化。
定义一个用于创建对象的接口,让子类决定实例化哪一个类。Factory Method使得一个类的实例化延迟(目的:解耦,手段:虚函数)到子类。------《设计模式》GoF
二.问题
想象你正在创建一个物流管理应用程序。你的应用第一版只能处理卡车运输,所以大部分代码都在该类里。Truck
过一段时间,你的应用会变得相当受欢迎。每天你都会收到数十个海运公司请求,希望将海运物流纳入应用。

好消息,对吧?那密码呢?目前,你的大部分代码是与类耦合的。要在应用中添加内容,就需要对整个代码库进行修改。此外,如果你以后决定在应用中添加其他类型的交通工具,可能需要再次进行所有这些调整。Truck``Ships
结果是,你会得到相当糟糕的代码,充满了根据运输对象类别切换应用行为的条件句。
三.解决方案
工厂方法模式建议你用作符替代直接对象构造调用,而调用一个特殊的工厂 方法。别担心:对象仍然是通过作符创建的,但调用是在工厂方法内部。通过工厂方法返回的对象通常被称为产品。

乍一看,这个改动似乎毫无意义:我们只是把构造调用从程序的一个部分移到了另一个部分。不过,请考虑:现在你可以在子类中覆盖工厂方法,并更改该方法生成的产品类别。
不过有个小限制:只有当这些产品有共同的基类或接口时,子类才能返回不同类型的产品。此外,基类中的工厂方法应声明其返回类型为该接口。
使用工厂方法的代码(通常称为客户端代码)看不到不同子类返回的实际产品之间的区别。客户将所有产品视为抽象的。客户端知道所有传输对象都应该有该方法,但具体如何工作对客户端来说并不重要。
四.工厂模式结构

五.工厂方法模式适合应用场景
-
当你事先不知道代码应该支持的具体对象类型和依赖关系时,可以使用工厂方法。
工厂方法将产品构建代码与实际使用产品的代码分开。因此,独立于其他代码扩展产品构建规范更容易。
例如,要向应用添加新产品类型,只需创建一个新的创作子类并覆盖其原厂方法。
-
当你想为库或框架的用户提供扩展其内部组件的方式时,可以使用工厂方法。
继承可能是扩展库或框架默认行为的最简单方式。但框架如何识别你的子类应该被用来代替标准组件呢?
解决方案是将构建组件的代码简化为单一工厂方法,允许任何人覆盖该方法,同时扩展组件本身。
让我们看看这会如何运作。想象一下,你用开源的UI框架编写一个应用。你的应用应该有圆形按钮,但框架只提供方形按钮。你用一个辉煌子职业扩展了标准职业。但现在你需要告诉主职业使用新的按钮子职业,而不是默认的。
Button``RoundButton``UIFramework为此,你需要从基础框架类创建一个子类并覆盖其方法。虽然这个方法返回基类中的对象,但你让子类返回对象。现在用 class 代替 。就这些了!
UIWithRoundButtons``createButton``Button``RoundButton``UIWithRoundButtons``UIFramework -
当你想通过重用现有对象来节省系统资源,而不是每次都重建时,可以使用工厂方法。
当处理大型、资源密集型对象,如数据库连接、文件系统和网络资源时,你经常会遇到这种需求。
让我们来思考一下,要重用一个现有的对象需要做些什么:
-
首先,你需要创建一个存储空间来跟踪所有已创建的对象。
-
当有人请求对象时,程序应在该池中寻找空闲对象。
-
...然后把它还给客户端代码。
-
如果没有空闲对象,程序应该创建一个新的对象(并将其添加到池中)。
代码真多!而且必须集中在一个地方,避免重复代码污染程序。
最明显且方便放置这些代码的地方可能是我们试图重用对象的类的构造函数。然而,构造函数必须根据定义始终返回新对象。它不能返回已有的实例。
因此,你需要有一套能够创建新对象和重复利用现有物品的常规方法。这听起来很像工厂的方法。
-
六.实现方式
-
让所有产品遵循相同的接口。该接口应声明在每个产品中都适用的方法。
-
在创建类中添加一个空的工厂方法。方法的返回类型应与通用的产品接口相匹配。
-
在创建者的代码中查找所有对产品构造器的引用。逐一用调用工厂方法替换它们,同时将产品创建代码提取到工厂方法中。
你可能需要在工厂方法中添加临时参数来控制退货产品类型。
此时,工厂方法的代码可能看起来相当难看。它可能有一个大语句,选择要实例化的产品类别。但别担心,我们很快就会解决的。
switch -
现在,为工厂方法中列出的每种产品类型创建一组创造子类。覆盖子类中的工厂方法,并从基础方法中提取相应的构造代码位。
-
如果产品类型太多,且为所有类型创建子类不合理,你可以在子类中重复使用基础类的控制参数。
例如,假设你有以下类层次结构:基类,包含几个子类:和;这些类分别是 、 和 。虽然该类只使用对象,但也可以同时处理和对象。你可以创建一个新的子类(比如)来处理这两种情况,但还有另一种选择。客户端代码可以将参数传递给该类的工厂方法,以控制它想要接收的产品。
Mail``AirMail``GroundMail``Transport``Plane``Truck``Train``AirMail``Plane``GroundMail``Truck``Train``TrainMail``GroundMail -
如果在所有提取后,基础工厂方法已经空了,你可以把它做成抽象的。如果还有剩余,你可以将其设为方法的默认行为。
七.优缺点
-
优点:
-
这样可以避免制造物和混凝土产品之间的紧密耦合。
-
单一责任原则。你可以把产品创建代码集中到程序的某个地方,这样代码支持起来更方便。
-
开闭原则。你可以在不破坏现有客户端代码的情况下引入新类型的产品。
-
-
缺点
- 代码可能会变得更复杂,因为你需要引入很多新的子类来实现这个模式。最好的情况是你把模式引入现有的创作者类别层级中。
八.与其他模式的关系
-
许多设计从工厂方法开始(简单且通过子职业更可定制),随后逐步发展为抽象工厂、原型或建造者(更灵活但更复杂)。
-
抽象工厂类通常基于一组工厂方法,但你也可以用Prototype来组合这些类的方法。
-
你可以用工厂方法和迭代器,让集合子类返回与集合兼容的不同类型的迭代器。
-
原型机不基于继承,所以没有缺点。另一方面,Prototype需要对克隆对象进行复杂的初始化。工厂方法基于继承,但不需要初始化步骤。
-
工厂法是模板法的一个专业化。同时,工厂方法也可以作为大型模板方法中的一个步骤。
九.示例代码
/**
* The Product interface declares the operations that all concrete products must
* implement.
*/
class Product {
public:
virtual ~Product() {}
virtual std::string Operation() const = 0;
};
/**
* Concrete Products provide various implementations of the Product interface.
*/
class ConcreteProduct1 : public Product {
public:
std::string Operation() const override {
return "{Result of the ConcreteProduct1}";
}
};
class ConcreteProduct2 : public Product {
public:
std::string Operation() const override {
return "{Result of the ConcreteProduct2}";
}
};
/**
* The Creator class declares the factory method that is supposed to return an
* object of a Product class. The Creator's subclasses usually provide the
* implementation of this method.
*/
class Creator {
/**
* Note that the Creator may also provide some default implementation of the
* factory method.
*/
public:
virtual ~Creator(){};
virtual Product* FactoryMethod() const = 0;
/**
* Also note that, despite its name, the Creator's primary responsibility is
* not creating products. Usually, it contains some core business logic that
* relies on Product objects, returned by the factory method. Subclasses can
* indirectly change that business logic by overriding the factory method and
* returning a different type of product from it.
*/
std::string SomeOperation() const {
// Call the factory method to create a Product object.
Product* product = this->FactoryMethod();
// Now, use the product.
std::string result = "Creator: The same creator's code has just worked with " + product->Operation();
delete product;
return result;
}
};
/**
* Concrete Creators override the factory method in order to change the
* resulting product's type.
*/
class ConcreteCreator1 : public Creator {
/**
* Note that the signature of the method still uses the abstract product type,
* even though the concrete product is actually returned from the method. This
* way the Creator can stay independent of concrete product classes.
*/
public:
Product* FactoryMethod() const override {
return new ConcreteProduct1();
}
};
class ConcreteCreator2 : public Creator {
public:
Product* FactoryMethod() const override {
return new ConcreteProduct2();
}
};
/**
* The client code works with an instance of a concrete creator, albeit through
* its base interface. As long as the client keeps working with the creator via
* the base interface, you can pass it any creator's subclass.
*/
void ClientCode(const Creator& creator) {
// ...
std::cout << "Client: I'm not aware of the creator's class, but it still works.\n"
<< creator.SomeOperation() << std::endl;
// ...
}
/**
* The Application picks a creator's type depending on the configuration or
* environment.
*/
int main() {
std::cout << "App: Launched with the ConcreteCreator1.\n";
Creator* creator = new ConcreteCreator1();
ClientCode(*creator);
std::cout << std::endl;
std::cout << "App: Launched with the ConcreteCreator2.\n";
Creator* creator2 = new ConcreteCreator2();
ClientCode(*creator2);
delete creator;
delete creator2;
return 0;
}
执行结果
App: Launched with the ConcreteCreator1.
Client: I'm not aware of the creator's class, but it still works.
Creator: The same creator's code has just worked with {Result of the ConcreteProduct1}
App: Launched with the ConcreteCreator2.
Client: I'm not aware of the creator's class, but it still works.
Creator: The same creator's code has just worked with {Result of the ConcreteProduct2}