工厂模式(C++编码)
工厂模式概念:用一个简单的类来创建实例的过程便称为工厂,用工厂方式代替外部new
操作的一种设计模式称为工厂模式。这是一种创建型模式,它提供了一个创建对象的最佳方式。在工厂模式中,我们创建对象时不会对上层暴露创建逻辑,而是通过使用一个共同结构来指向新创建的对象。
工厂模式分类:简单工厂模式、工厂方法模式、抽象工厂模式。
意图:定义一个常见对象的接口,让子类自己决定实例化哪个工厂类,工厂模式使其创建过程延迟到子类进行。
问题解决:主要解决接口选择问题。
解决方法:让其子类实现工厂接口,返回的是一个抽象的产品。
使用前提:1、编码时不能预见需要创建哪种类的实例(不同的条件下发创建不同实例);2、系统不应依赖于产品类实例如何被创建、组合和表达的细节。
关键代码:子类执行创建过程。
优点:1、使代码结构清晰,有效地封装变化,提高拓展性;2、屏蔽产品的具体实现,调用者只关心产品的接口;3、降低耦合度。
二、各类工厂模式讲解及实现
1、简单工厂模式
(1)概述
简单工厂模式时属于创建型模式,又叫做静态工厂方法模式,简单工厂模式实现由一个工厂对象决定创建出来指定产品类的实例。
(2)特点
简单工厂模式由一个工厂类根据传入的参数,动态决定应该创建哪一种产品类实例。简单工厂模式需要在工厂类中作出判断,在创建产品的时候指定响应产品创造。
(3)类图
(4)例子
假设有个工厂能生产出 A、B 两种产品,当客户需要产品的时候明确告知工厂生产哪类产品,工厂需要接收顾客提供的类别信息,当新增产品的时候,工厂内部去添加新产品的生产方式。
(5)应用说明
一个工厂,多个产品,产品需要有一个虚基类,通过接收类别参数生产具体产品,利用基类指针指向此对象。
(6)编码
1 #include <iostream> 2 3 enum ProductType 4 { 5 PRODUCT_A, 6 PRODUCT_B 7 }; 8 9 // 产品基类 10 class Product 11 { 12 public: 13 virtual void Show() = 0; 14 }; 15 16 // 产品 A 17 class ProductA : public Product 18 { 19 public: 20 void Show() 21 { 22 std::cout << "Product A." << std::endl; 23 } 24 }; 25 26 // 产品 B 27 class ProductB : public Product 28 { 29 public: 30 void Show() 31 { 32 std::cout << "Porduct B." << std::endl; 33 } 34 }; 35 36 // 工厂 37 class Factory 38 { 39 public: 40 Product* Create(int type) 41 { 42 switch(type) 43 { 44 case PRODUCT_A : return new ProductA; break; 45 case PRODUCT_B : return new ProductB; break; 46 47 default : break; 48 } 49 } 50 }; 51 52 int main() 53 { 54 Factory *factory = new Factory(); 55 56 factory->Create(PRODUCT_A)->Show(); 57 factory->Create(PRODUCT_B)->Show(); 58 59 return 0; 60 }
(7)缺点
工厂类集中了所有实例的创建逻辑,违反了高内聚责任分配原则,将全部创建逻辑集中到了一个工厂类中;它所能创建的类只能是事先考虑到的,如果需要添加新的类,则就需要改变工厂类了,违背开闭原则。当系统中的具体产品类不断增多时候,可能会出现要求工厂类根据不同条件创建不同实例的需求.这种对条件的判断和对具体产品类型的判断交错在一起,很难避免模块功能的蔓延,对系统的维护和扩展非常不利。
2、工厂方法模式
(1)概述
多个工厂,多个产品,每个产品对应一个工厂,此时工厂和产品都通过虚基类方式构建。
(2)特点
定义一个用于创建对象的接口,让子类决定实例化哪一个类。当增加一个新产品时,同时增加一个新工厂。增加新工厂属于扩展,不会修改以前工厂类和产品类的任何代码。可以看过多个独立的简单工厂模式构成了工厂方法模式。工厂方法让类的实例化推迟到子类中进行。
(3)类图
(4)例子
假设现在有 A、B 两种产品,则开两个工厂,工厂 A 负责生产产品 A,工厂 B 负责生产产品 B,客户只知道创建产品的工厂名,而不知道具体的产品名,工厂不需要再接收客户的产品类别,而只去生产产品。
(5)应用说明
把简单工厂模式中的工厂类抽象出一个结构,这个接口只有一个创建抽象产品的工厂方法,工厂方法模式解决了简单工厂模式中出现的缺点。
(6)编码
1 #include <iostream> 2 3 // 产品基类 4 class Product 5 { 6 public: 7 virtual void Show() = 0; 8 }; 9 10 // 工厂基类 11 class Factory 12 { 13 public: 14 virtual Product* Create() = 0; 15 }; 16 17 // 产品 A 18 class ProductA : public Product 19 { 20 public: 21 void Show() 22 { 23 std::cout << "Product A." << std::endl; 24 } 25 }; 26 27 // 产品 B 28 class ProductB : public Product 29 { 30 public: 31 void Show() 32 { 33 std::cout << "Porduct B." << std::endl; 34 } 35 }; 36 37 // 工厂 A 38 class FactoryA : public Factory 39 { 40 public: 41 Product* Create() 42 { 43 return new ProductA; 44 } 45 }; 46 47 // 工厂 B 48 class FactoryB : public Factory 49 { 50 public: 51 Product* Create() 52 { 53 return new ProductB; 54 } 55 }; 56 57 int main() 58 { 59 FactoryA *factoryA = new FactoryA(); 60 FactoryB *factoryB = new FactoryB(); 61 62 factoryA->Create()->Show(); 63 factoryB->Create()->Show(); 64 65 return 0; 66 }
(7)适用场景
工厂方法模式和简单工厂模式虽然都是通过工厂来创建对象,他们之间最大的不同是——工厂方法模式在设计上完全完全符合开闭原则。适用场景:1、一个类不知道它所需要的对象的类(在工厂方法模式中,客户端不需要知道具体产品类的类名,只需要知道所对应的工厂即可,具体的产品对象由具体工厂类创建;客户端需要知道创建具体产品的工厂类);2、一个类通过其子类来指定创建哪个对象(在工厂方法模式中,对于抽象工厂类只需要提供一个创建产品的接口,而由其子类来确定具体要创建的对象,利用面向对象的多态性和里氏代换原则,在程序运行时,子类对象将覆盖父类对象,从而使得系统更容易扩展);3、将创建对象的任务委托给多个工厂子类中的某一个,客户端在使用时可以无须关心是哪一个工厂子类创建产品子类,需要时再动态指定,可将具体工厂类的类名存储在配置文件或数据库中。
(8)具体应用
-
日志记录器:记录可能记录到本地硬盘、系统事件、远程服务器等,用户可以选择记录日志到什么地方。
-
数据库访问,当用户不知道最后系统采用哪一类数据库,以及数据库可能有变化时。
-
设计一个连接服务器的框架,需要三个协议,"POP3"、"IMAP"、"HTTP",可以把这三个作为产品类,共同实现一个接口。
(9)缺点
每次增加一个产品时,都需要增加一个具体类和对象实现工厂,使得系统中类的个数成倍增加,在一定程度上增加了系统的复杂度,同时也增加了系统具体类的依赖。
3、抽象工厂模式
(1)概述
工厂方法模式通过引入工厂等级结构,解决了简单工厂模式中工厂类职责太重的问题,但由于工厂方法模式中的每个工厂只生产一类产品,可能会导致系统中存在大量的工厂类,势必会增加系统的开销。此时,我们可以考虑将一些相关的产品组成一个产品族(位于不同产品等级结构中功能相关联的产品组成的家族),由同一个工厂来统一生产,这就是抽象工厂模式的基本思想。
(2)特点
提供一个创建一系列相关或相互依赖对象的接口,而无需指定它们具体的类。
(3)类图
(4)例子
假如 A 产品中有 A1 和 A2 两种型号的产品,B 产品中有 B1 和 B2 两种型号的产品,这个时候使用抽象工厂模式,还是开设两家工厂,工厂等级为 1 则负责生产 A1 、B1 型号产品,工厂等级为 2 则负责生产 A2、B2 型号的产品。这样减少了工厂类和产品类在项目中的数量,并完成针对多个产品等级结构的抽象。
(5)编码
1 #include <iostream> 2 3 // Product A 4 class ProductA 5 { 6 public: 7 virtual void Show() = 0; 8 }; 9 10 class ProductA1 : public ProductA 11 { 12 public: 13 void Show() 14 { 15 std::cout << "Product A1." << std::endl ; 16 } 17 }; 18 19 class ProductA2 : public ProductA 20 { 21 public: 22 void Show() 23 { 24 std::cout << "Product A2." << std::endl ; 25 } 26 }; 27 28 // Product B 29 class ProductB 30 { 31 public: 32 virtual void Show() = 0; 33 }; 34 35 class ProductB1 : public ProductB 36 { 37 public: 38 void Show() 39 { 40 std::cout << "Product B1." << std::endl ; 41 } 42 }; 43 44 class ProductB2 : public ProductB 45 { 46 public: 47 void Show() 48 { 49 std::cout << "Product B2." << std::endl ; 50 } 51 }; 52 53 // Factory 54 class Factory 55 { 56 public: 57 virtual ProductA* CreateProductA() = 0; 58 virtual ProductB* CreateProductB() = 0; 59 }; 60 61 class Factory1 : public Factory 62 { 63 public: 64 ProductA* CreateProductA() 65 { 66 return new ProductA1(); 67 } 68 69 ProductB* CreateProductB() 70 { 71 return new ProductB1(); 72 } 73 }; 74 75 class Factory2 : public Factory 76 { 77 ProductA* CreateProductA() 78 { 79 return new ProductA2(); 80 } 81 82 ProductB* CreateProductB() 83 { 84 return new ProductB2(); 85 } 86 }; 87 88 int main() 89 { 90 Factory *factoryObj1 = new Factory1(); 91 ProductA *productObjA1 = factoryObj1->CreateProductA(); 92 ProductB *productObjB1 = factoryObj1->CreateProductB(); 93 94 productObjA1->Show(); 95 productObjB1->Show(); 96 97 Factory *factoryObj2 = new Factory2(); 98 ProductA *productObjA2 = factoryObj2->CreateProductA(); 99 ProductB *productObjB2 = factoryObj2->CreateProductB(); 100 101 productObjA2->Show(); 102 productObjB2->Show(); 103 104 return 0; 105 }
(6)适用场景
-
一个系统不应当依赖于产品类实例如何被创建、组合和表达的细节,这对于所有类型的工厂模式都是重要的。
-
系统中有多于一个的产品族,而每次只使用其中某一产品族。
-
属于同一个产品族的产品将在一起使用,这一约束必须在系统的设计中体现出来。
-
系统结构稳定,不会频繁的增加对象。
(7)开闭原则的倾斜性
在抽象工厂模式中,增加新的产品族很方便,但是增加新的产品等级结构很麻烦,抽象工厂模式的这种性质称为开闭原则的倾斜性。开闭原则要求系统对扩展开放,对修改封闭,通过扩展达到增强其功能的目的,对于涉及到多个产品族与多个产品等级结构的系统,其功能增强包括两方面:1、增加产品族——对于增加新的产品族,工厂方法模式很好的支持了开闭原则,对于新增加的产品族,只需要对应增加一个新的具体工厂即可,对已有代码无须做任何修改。2、增加新的产品等级结构——对于增加新的产品等级结构,需要修改所有的工厂角色,包括抽象工厂类,在所有的工厂类中都需要增加生产新产品的方法,违背了开闭原则。
正因为抽象工厂模式存在开闭原则的倾斜性,它以一种倾斜的方式来满足开闭原则,为增加新产品族提供方便,但不能为增加新产品结构提供这样的方便,因此要求设计人员在设计之初就能够全面考虑,不会在设计完成之后向系统中增加新的产品等级结构,也不会删除已有的产品等级结构,否则将会导致系统出现较大的修改,为后续维护工作带来诸多麻烦。
(8)优点
易于交换产品系列,由于具体工厂类在一个应用中只需要在初始化的时候出现一次,这样就使得改变一个应用的具体工厂变得非常容易,只需要改变具体工厂即可使用不同的产品配置。让具体的创建实例过程与客户端分离,客户端是通过它们的抽象接口操纵实例,产品的具体类名也被具体工厂实现分离,不会出现在客户代码中。
(9)缺点
增加新的产品等级结构麻烦,需要对原有系统进行较大的修改,甚至需要修改抽象层代码,这显然会带来较大的不便,违背了“开闭原则”。
三、工厂模式的退化过程
四、Reference
1、C++工厂模式分析和总结(阿里云开发者社区):
2、工厂模式——这一篇就够了(InfoQ写作平台):
3、23种设计模式全面解析(C语言中文网):