@[TOC](工厂模式(Factory Method))
写在前面
对象创建模式
通过对象超级模式绕开。动态内存分配(new),来避免对象创建过程中所导致的紧耦合(依赖具体类),从而支持对象创建的稳定,它是结构抽象之后的第一步工作。
典型模式:
- 工厂方法(Factory Method Pattern)
- 抽象工厂(Abstract Factory Pattern)
提供一个接口,用于创建相关或依赖对象的家族,而不需要指定具体的类。 - 原型模式(Prototype Pattern)
将一个复杂对象的构建过程与其表示分离,使得同样的构建过程可以创建不同的表示。 - 构建器(Builder Pattern)
通过复制现有对象来创建新对象,而不是通过实例化类来创建新对象
对象创建模式(Creational Patterns)是设计模式的一类,主要关注对象的创建过程。它们提供了一种在系统中创建对象的最佳方法,而不是通过直接实例化类的方式。
动机
在软件系统中经常面临着创建对象的工作,由于需求的变化,需要创建的对象的具体类型经常发生变化。
但是如何应对这种变化呢?如何绕过常规的对象创建方法,提供一种封装机制来避免客户程序和这种具体对象创建工作的紧耦合?
那么工厂模式就是为了解决这类问题。
代码推导
class { public: virtual void split()=0; virtual ~ISplitter(){} }; class BinarySplitter : public ISplitter{ }; class TxtSplitter: public ISplitter{ }; class PictureSplitter: public ISplitter{ }; class VideoSplitter: public ISplitter{ }; class MainForm : public Form { public: void Button1_Click(){ ISplitter * splitter= new BinarySplitter();//依赖具体类 splitter->split(); } };
这段代码展示了一个简单的面向对象设计,其中包含了一个用于演示不同分割器的类结构。具体类 MainForm
依赖于具体的分割器类,展示了如何使用这些类来进行操作。
1. 抽象类 ISplitter
class ISplitter { public: virtual void split() = 0; virtual ~ISplitter() {} };
ISplitter
是一个抽象类(接口类),定义了一个纯虚函数split()
,表示分割器对象需要实现的行为。- 纯虚函数(
= 0
)使得ISplitter
成为抽象类,不能直接实例化,只能通过继承来实现。 - 虚析构函数
~ISplitter()
确保派生类对象被正确销毁。
2. 具体的分割器类
class BinarySplitter : public ISplitter { public: void split() override { // 实现二进制文件的分割 } }; class TxtSplitter : public ISplitter { public: void split() override { // 实现文本文件的分割 } }; class PictureSplitter : public ISplitter { public: void split() override { // 实现图片文件的分割 } }; class VideoSplitter : public ISplitter { public: void split() override { // 实现视频文件的分割 } };
BinarySplitter
、TxtSplitter
、PictureSplitter
和VideoSplitter
是ISplitter
的具体实现类,每个类都实现了split()
方法,用于处理不同类型的文件分割。
3. 主窗体类 MainForm
class MainForm : public Form { public: void Button1_Click() { ISplitter *splitter = new BinarySplitter(); // 依赖具体类 splitter->split(); delete splitter; // 不要忘记释放内存 } };
MainForm
继承自Form
类(假设Form
类定义了 GUI 窗体的基本功能)。Button1_Click()
方法模拟了一个按钮点击事件,在这个事件中创建了一个BinarySplitter
对象并调用其split()
方法进行分割操作。delete splitter
用于释放内存,防止内存泄漏。
代码的缺陷
- 硬编码依赖:
MainForm
类中硬编码了具体的BinarySplitter
类,这导致代码的灵活性和可扩展性差。如果需要更换分割器类型,需要修改MainForm
的代码。 - 违反开闭原则: 如果需要添加新的分割器类型,需要修改现有代码,这违反了开闭原则(对扩展开放,对修改关闭)。
- 缺少内存管理: 虽然
delete splitter
释放了内存,但如果split()
方法中抛出异常,会导致内存泄漏。因此,推荐使用智能指针来自动管理内存。
从上面的代码可以看出,Button1_Click函数中的ISplitter 指针需要依赖new出具体的类。来调用split函数。那么问题来了,如何绕开new? 就是我们需要一个接口创建具体的类。而且这个接口也是动态的,也就是虚函数。能实现代码中的依赖倒置原则中的具体依赖抽象。但是此时我们不可能再基类中拥有这个结构。
直接在 ISplitter
中声明 CreateSplitter
接口会引入一些设计上的问题和违反面向对象设计的原则。下面是一些关键原因:
职责单一原则(Single Responsibility Principle, SRP):
ISplitter
的职责应该是定义分割操作的接口,而不是负责创建分割器实例。将创建逻辑和分割逻辑混合在同一个接口中,会导致接口的职责过多,不符合职责单一原则。
接口隔离原则(Interface Segregation Principle, ISP):
ISplitter
接口的用户可能只需要调用split
方法,而不关心如何创建分割器实例。如果将创建方法添加到ISplitter
中,所有实现ISplitter
的类都必须实现这个方法,这会增加实现的复杂性,并且违反接口隔离原则。
工厂模式的作用:
- 工厂模式旨在封装对象创建的过程,使得创建逻辑与使用逻辑分离。如果将创建逻辑直接放在
ISplitter
接口中,就无法利用工厂模式的优势,例如替换具体实现、延迟实例化等。
- 工厂模式旨在封装对象创建的过程,使得创建逻辑与使用逻辑分离。如果将创建逻辑直接放在
可扩展性和灵活性:
- 通过将创建逻辑放在工厂类中,可以在不修改现有代码的情况下添加新的分割器类型。这有助于遵循开闭原则(对扩展开放,对修改关闭)。如果将创建逻辑放在
ISplitter
中,添加新的分割器类型就需要修改接口及其所有实现,降低了代码的可扩展性。
- 通过将创建逻辑放在工厂类中,可以在不修改现有代码的情况下添加新的分割器类型。这有助于遵循开闭原则(对扩展开放,对修改关闭)。如果将创建逻辑放在
不推荐的设计:将创建方法放在 ISplitter
中
class ISplitter { public: virtual void split() = 0; virtual ISplitter* CreateSplitter() = 0; virtual ~ISplitter() {} }; class BinarySplitter : public ISplitter { public: void split() override { // 实现二进制文件的分割 } ISplitter* CreateSplitter() override { return new BinarySplitter(); } }; // MainForm 依赖具体实现,违背单一职责原则和工厂模式的初衷 class MainForm : public Form { public: void Button1_Click() { ISplitter *splitter = new BinarySplitter(); // 或调用 splitter->CreateSplitter() splitter->split(); delete splitter; } };
此时需要新建一个接口基类,还存放CreateSplitter函数,此时就要用到工厂模式。
改进建议
可以使用工厂模式或依赖注入来解决上述缺陷,使得 MainForm
不依赖具体的分割器类,从而提高代码的灵活性和可扩展性。例如:
使用工厂模式改进
class SplitterFactory { public: virtual ISplitter* CreateSplitter() = 0; virtual ~SplitterFactory() {} }; class BinarySplitterFactory : public SplitterFactory { public: ISplitter* CreateSplitter() override { return new BinarySplitter(); } }; class MainForm : public Form { private: SplitterFactory *factory; public: MainForm(SplitterFactory *f) : factory(f) {} void Button1_Click() { ISplitter *splitter = factory->CreateSplitter(); splitter->split(); delete splitter; } };
通过使用 SplitterFactory
,可以在运行时决定创建哪种类型的分割器,而不需要修改 MainForm
的代码,提高了系统的灵活性和可维护性。
模式定义
定义一个用于创建对象的接口,让子类决定实例化哪一个类。工厂模式使得一个类的实例化得到了延迟到子类中。这样使用虚函数的方式目的在于能够解耦和。
模式结构
简单工厂模式
+-----------------+ | SimpleFactory | +-----------------+ | + createProduct(type: String): Product | +-----------------+ | | +-------+-------+ | | v v +----------------+ +----------------+ | ConcreteProductA | | ConcreteProductB | +----------------+ +----------------+ | + use() | | + use() | +----------------+ +----------------+ ^ ^ | | +--------+-------+ | +------+ |Product| +------+ |+use() | +------+
工厂方法模式
+-----------------+ | Factory | +-----------------+ | + createProduct() : Product | +-----------------+ | | +-------+-------+ | | v v +----------------+ +----------------+ |ConcreteFactoryA| |ConcreteFactoryB| +----------------+ +----------------+ |+createProduct(): Product|+createProduct(): Product| +----------------+ +----------------+ | | v v +----------------+ +----------------+ | ConcreteProductA | | ConcreteProductB | +----------------+ +----------------+ | + use() | | + use() | +----------------+ +----------------+ ^ ^ | | +--------+-------+ | +------+ |Product| +------+ |+use() | +------+
抽象工厂模式
+--------------------+ | AbstractFactory | +--------------------+ | + createProductA(): ProductA | | + createProductB(): ProductB | +--------------------+ | +-------+-------+ | | v v +----------------+ +----------------+ | ConcreteFactory1| | ConcreteFactory2| +----------------+ +----------------+ |+createProductA(): ProductA|+createProductA(): ProductA| |+createProductB(): ProductB|+createProductB(): ProductB| +----------------+ +----------------+ | | | | v v +----------------+ +----------------+ | ConcreteProductA1 | | ConcreteProductA2 | +----------------+ +----------------+ | + use() | | + use() | +----------------+ +----------------+ ^ ^ | | +--------+-------+ | +------+ |ProductA| +------+ |+use() | +------+ | | v v +----------------+ +----------------+ | ConcreteProductB1 | | ConcreteProductB2 | +----------------+ +----------------+ | + use() | | + use() | +----------------+ +----------------+ ^ ^ | | +--------+-------+ | +------+ |ProductB| +------+ |+use() | +------+
这些图示展示了各个设计模式的主要组件及其关系,希望能帮助你更好地理解工厂模式的结构。
工厂模式(Factory Pattern)是一种创建型设计模式,它提供了一种创建对象的方式,而无需在代码中显式指定要创建的具体类。工厂模式可以分为简单工厂模式、工厂方法模式和抽象工厂模式。下面分别介绍这些模式的结构。
简单工厂模式
简单工厂模式通常也被称为静态工厂方法(Static Factory Method)模式,它通过一个工厂类来创建对象。这个工厂类包含一个静态方法,根据输入的参数来决定创建哪种具体类的实例。
结构:
- 工厂类(Factory):包含一个静态方法,用于根据不同的条件创建不同的对象。
- 产品类(Product):工厂创建的对象的父类或接口。
- 具体产品类(ConcreteProduct):工厂类实际创建的具体对象。
工厂方法模式
工厂方法模式通过定义一个创建对象的接口,让子类决定实例化哪一个类。工厂方法使一个类的实例化延迟到其子类。
结构:
- 抽象工厂类(Factory):声明一个创建产品对象的工厂方法,返回产品的实例。
- 具体工厂类(ConcreteFactory):实现创建产品对象的工厂方法。
- 产品类(Product):工厂创建的对象的父类或接口。
- 具体产品类(ConcreteProduct):工厂类实际创建的具体对象。
旨在将对象的创建过程封装起来,使得客户端代码可以依赖于接口而不是具体的实现类,从而提高代码的可维护性和可扩展性。
要点总结
- 工厂方法模式用于隔离类对象的使用者和使用类型之间的耦合关系。面对一个经常变化的具体类型。紧耦合关系会导致软件的脆弱。
- 工厂方法模式,通过面向对象的手法将所要创建的具体对象工作延时到此类,从而实现一种扩展而非更改的策略,较好地解决了这种紧耦合的关系。
- 工厂方法模式解决单个对象的需求变化。缺点在于要求创建方法的参数相同。
好