工厂模式详解从简单到抽象C完整实现引言在软件开发中创建对象是我们每天都在做的事情。但当对象的创建逻辑变得复杂或者需要根据不同条件创建不同类型的对象时直接使用new关键字会导致代码耦合度高、难以维护和扩展。工厂模式正是为了解决这个问题而生的。它是一种创建型设计模式提供了一种封装对象创建过程的方式将对象的创建与使用分离。这使得代码更加灵活符合开闭原则对扩展开放对修改关闭。工厂模式主要分为三种简单工厂模式Simple Factory工厂方法模式Factory Method抽象工厂模式Abstract Factory今天我们就用C语言从最简单的简单工厂开始一步步深入理解这三种工厂模式。一、简单工厂模式1.1 概念简单工厂模式是工厂模式中最简单的一种。它定义了一个工厂类负责根据传入的参数动态决定创建哪一个产品类的实例。核心思想把对象的创建逻辑集中到一个工厂类中客户端不需要知道具体的创建细节只需要告诉工厂需要什么类型的产品即可。1.2 UML类图---------------- ---------------- | Client | ---- | SimpleFactory | ---------------- ---------------- | v ----------- | Product | -- 抽象产品 ----------- ^ ^ / \ / \ ----------- ----------- | ProductA | | ProductB | -- 具体产品 ----------- -----------1.3 C实现我们以图形绘制为例来实现简单工厂模式。我们有不同类型的图形圆形、矩形、三角形需要一个工厂来根据用户的需求创建对应的图形对象。#includeiostream#includestring// 抽象产品类图形classShape{public:virtual~Shape()default;virtualvoiddraw()const0;// 纯虚函数绘制图形};// 具体产品类圆形classCircle:publicShape{public:voiddraw()constoverride{std::cout绘制一个圆形std::endl;}};// 具体产品类矩形classRectangle:publicShape{public:voiddraw()constoverride{std::cout绘制一个矩形std::endl;}};// 具体产品类三角形classTriangle:publicShape{public:voiddraw()constoverride{std::cout绘制一个三角形std::endl;}};// 简单工厂类图形工厂classShapeFactory{public:// 根据类型创建对应的图形对象Shape*createShape(conststd::stringtype){if(typecircle){returnnewCircle();}elseif(typerectangle){returnnewRectangle();}elseif(typetriangle){returnnewTriangle();}else{std::cerr错误不支持的图形类型std::endl;returnnullptr;}}};// 客户端代码intmain(){ShapeFactory factory;Shape*circlefactory.createShape(circle);if(circle){circle-draw();deletecircle;}Shape*rectanglefactory.createShape(rectangle);if(rectangle){rectangle-draw();deleterectangle;}Shape*trianglefactory.createShape(triangle);if(triangle){triangle-draw();deletetriangle;}return0;}1.4 运行结果绘制一个圆形 绘制一个矩形 绘制一个三角形1.5 优缺点分析优点实现了对象创建和使用的分离客户端不需要知道具体产品类的类名只需要知道对应的参数一定程度上提高了系统的灵活性缺点违反了开闭原则如果要添加新的产品类型必须修改工厂类的createShape方法工厂类职责过重包含了所有产品的创建逻辑一旦工厂类出现问题整个系统都会受到影响产品类型过多时工厂类会变得非常庞大难以维护二、工厂方法模式2.1 概念为了解决简单工厂模式违反开闭原则的问题我们引入了工厂方法模式。工厂方法模式定义了一个创建对象的接口但让子类决定实例化哪一个类。工厂方法使一个类的实例化延迟到其子类。核心思想不再有一个统一的工厂类来创建所有产品而是为每个产品都提供一个对应的工厂类。这些工厂类都实现了同一个抽象工厂接口。2.2 UML类图---------------- ---------------- | Client | ---- | Factory | -- 抽象工厂 ---------------- ---------------- ^ ^ / \ / \ ---------------- ---------------- | CircleFactory | | RectangleFactory| -- 具体工厂 ---------------- ---------------- | | v v ----------- ----------- | Circle | | Rectangle | -- 具体产品 ----------- ----------- ^ | ----------- | Shape | -- 抽象产品 -----------2.3 C实现我们继续使用图形绘制的例子将其改造成工厂方法模式。#includeiostream#includestring// 抽象产品类图形classShape{public:virtual~Shape()default;virtualvoiddraw()const0;};// 具体产品类圆形classCircle:publicShape{public:voiddraw()constoverride{std::cout绘制一个圆形std::endl;}};// 具体产品类矩形classRectangle:publicShape{public:voiddraw()constoverride{std::cout绘制一个矩形std::endl;}};// 具体产品类三角形classTriangle:publicShape{public:voiddraw()constoverride{std::cout绘制一个三角形std::endl;}};// 抽象工厂类图形工厂classShapeFactory{public:virtual~ShapeFactory()default;virtualShape*createShape()0;// 工厂方法};// 具体工厂类圆形工厂classCircleFactory:publicShapeFactory{public:Shape*createShape()override{returnnewCircle();}};// 具体工厂类矩形工厂classRectangleFactory:publicShapeFactory{public:Shape*createShape()override{returnnewRectangle();}};// 具体工厂类三角形工厂classTriangleFactory:publicShapeFactory{public:Shape*createShape()override{returnnewTriangle();}};// 客户端代码intmain(){// 创建圆形工厂生产圆形ShapeFactory*circleFactorynewCircleFactory();Shape*circlecircleFactory-createShape();circle-draw();deletecircle;deletecircleFactory;// 创建矩形工厂生产矩形ShapeFactory*rectangleFactorynewRectangleFactory();Shape*rectanglerectangleFactory-createShape();rectangle-draw();deleterectangle;deleterectangleFactory;// 创建三角形工厂生产三角形ShapeFactory*triangleFactorynewTriangleFactory();Shape*triangletriangleFactory-createShape();triangle-draw();deletetriangle;deletetriangleFactory;return0;}2.4 运行结果绘制一个圆形 绘制一个矩形 绘制一个三角形2.5 优缺点分析优点完全符合开闭原则添加新的产品类型时只需要添加对应的产品类和工厂类不需要修改现有代码每个工厂只负责创建一种产品职责单一降低了代码的耦合度客户端只依赖抽象工厂和抽象产品缺点类的数量成倍增加每添加一个产品就需要添加一个对应的工厂类增加了系统的抽象性和理解难度客户端需要知道使用哪个具体工厂来创建产品三、抽象工厂模式3.1 概念抽象工厂模式是工厂方法模式的进一步扩展。它提供了一个接口用于创建一系列相关或相互依赖的对象而无需指定它们具体的类。核心思想工厂方法模式只能生产一种产品而抽象工厂模式可以生产一系列相关的产品产品族。产品族指的是位于不同产品等级结构中功能相关联的产品组成的家族。例如在GUI库中Windows风格的按钮、文本框、滚动条就构成了一个产品族Mac风格的按钮、文本框、滚动条构成了另一个产品族。3.2 UML类图---------------- --------------------- | Client | ---- | AbstractFactory | -- 抽象工厂 ---------------- --------------------- ^ ^ / \ / \ --------------------- --------------------- | WindowsUIFactory | | MacUIFactory | -- 具体工厂 --------------------- --------------------- | | | | v v v v ----------- ----------- ----------- ----------- | WinButton | | WinTextBox| | MacButton | | MacTextBox| -- 具体产品 ----------- ----------- ----------- ----------- ^ ^ ^ ^ | | | | ----------- ----------- | Button | | TextBox | -- 抽象产品 ----------- -----------3.3 C实现我们以跨平台UI组件库为例来实现抽象工厂模式。我们需要创建不同风格Windows、Mac的按钮和文本框。#includeiostream#includestring// 抽象产品A按钮classButton{public:virtual~Button()default;virtualvoidrender()const0;};// 具体产品A1Windows风格按钮classWinButton:publicButton{public:voidrender()constoverride{std::cout渲染Windows风格的按钮std::endl;}};// 具体产品A2Mac风格按钮classMacButton:publicButton{public:voidrender()constoverride{std::cout渲染Mac风格的按钮std::endl;}};// 抽象产品B文本框classTextBox{public:virtual~TextBox()default;virtualvoidrender()const0;};// 具体产品B1Windows风格文本框classWinTextBox:publicTextBox{public:voidrender()constoverride{std::cout渲染Windows风格的文本框std::endl;}};// 具体产品B2Mac风格文本框classMacTextBox:publicTextBox{public:voidrender()constoverride{std::cout渲染Mac风格的文本框std::endl;}};// 抽象工厂UI工厂classUIFactory{public:virtual~UIFactory()default;virtualButton*createButton()0;virtualTextBox*createTextBox()0;};// 具体工厂1Windows UI工厂classWinUIFactory:publicUIFactory{public:Button*createButton()override{returnnewWinButton();}TextBox*createTextBox()override{returnnewWinTextBox();}};// 具体工厂2Mac UI工厂classMacUIFactory:publicUIFactory{public:Button*createButton()override{returnnewMacButton();}TextBox*createTextBox()override{returnnewMacTextBox();}};// 客户端代码classApplication{private:UIFactory*factory;Button*button;TextBox*textBox;public:Application(UIFactory*f):factory(f){buttonfactory-createButton();textBoxfactory-createTextBox();}~Application(){deletebutton;deletetextBox;deletefactory;}voidrenderUI(){button-render();textBox-render();}};intmain(){std::cout Windows 风格界面 std::endl;Application*winAppnewApplication(newWinUIFactory());winApp-renderUI();deletewinApp;std::cout\n Mac 风格界面 std::endl;Application*macAppnewApplication(newMacUIFactory());macApp-renderUI();deletemacApp;return0;}3.4 运行结果 Windows 风格界面 渲染Windows风格的按钮 渲染Windows风格的文本框 Mac 风格界面 渲染Mac风格的按钮 渲染Mac风格的文本框3.5 优缺点分析优点确保同一产品族的产品相互匹配将产品的创建代码与使用代码分离符合开闭原则添加新的产品族时只需要添加对应的具体工厂类和产品类符合单一职责原则每个工厂只负责创建同一产品族的产品缺点难以扩展产品等级结构如果要在产品族中添加一个新的产品类型例如滚动条需要修改抽象工厂接口以及所有的具体工厂类增加了系统的抽象性和理解难度类的数量更多系统更加庞大四、三种工厂模式对比模式核心思想适用场景优点缺点简单工厂一个工厂类创建所有产品产品类型较少且变化不频繁的场景实现简单客户端调用方便违反开闭原则工厂类职责过重工厂方法每个产品对应一个工厂类产品类型较多且经常需要扩展的场景符合开闭原则职责单一类的数量成倍增加系统复杂度提高抽象工厂一个工厂类创建一个产品族的所有产品需要创建一系列相关产品的场景确保产品族的一致性符合开闭原则难以扩展产品等级结构五、适用场景总结简单工厂模式适用场景工厂类负责创建的对象比较少客户端只知道传入工厂类的参数不关心如何创建对象产品类型相对稳定不会频繁增加工厂方法模式适用场景客户端不知道它所需要的对象的类一个类通过其子类来指定创建哪个对象需要灵活地扩展产品类型抽象工厂模式适用场景系统需要独立于产品的创建、组合和表示系统需要配置多个产品族中的一个需要强调一系列相关产品对象的设计以进行联合使用提供一个产品类库只想显示它们的接口而不是实现六、现代C改进建议在现代CC11及以后中我们可以对工厂模式进行一些改进使用智能指针避免手动管理内存防止内存泄漏#includememorystd::unique_ptrShapecreateShape()override{returnstd::make_uniqueCircle();}使用枚举代替字符串避免字符串拼写错误enumclassShapeType{CIRCLE,RECTANGLE,TRIANGLE};使用模板工厂可以进一步简化工厂方法模式的实现templatetypenameTclassTemplateFactory:publicShapeFactory{public:std::unique_ptrShapecreateShape()override{returnstd::make_uniqueT();}};// 使用autocircleFactorystd::make_uniqueTemplateFactoryCircle();七、总结工厂模式是设计模式中最常用的模式之一它的核心思想是将对象的创建与使用分离。通过使用工厂模式我们可以降低代码的耦合度提高代码的可维护性和可扩展性更好地遵循开闭原则和单一职责原则在实际开发中我们需要根据具体的业务场景选择合适的工厂模式如果产品类型很少且变化不频繁使用简单工厂模式如果产品类型较多且经常需要扩展使用工厂方法模式如果需要创建一系列相关的产品使用抽象工厂模式记住设计模式不是银弹不要为了使用模式而使用模式。只有在合适的场景下使用合适的设计模式才能真正发挥它的价值。