C++设计模式-工厂(2)
设计模式:在解决某一类问题场景时,有既定的,优秀的代码框架可以直接使用,其优势如下:
- 代码更易于维护,代码的可读性,复用性,可移植性,健壮性会更好
- 当软件原有需求有变更或者增加新的需求时,合理的设计模式的应用,能够做到软件设计要求的
开-闭原则
,即对修改关闭,对扩展开放,使软件原有功能修改,新功能扩充非常灵活
- 合理的设计模式的选择,会使软件设计更加模块化,积极的做到软件设计遵循的根本原则
高内聚,低耦合
软件设计开-闭原则
:
开闭原则是软件设计中的一个重要原则,指的是软件实体(如类、模块、函数等)应该对扩展开放,对修改关闭。这意味着在不修改现有代码的基础上,通过扩展新功能来满足新的需求,从而提高软件的灵活性和可维护性
对扩展开放:允许在现有系统中添加新功能
对修改关闭:不需要修改现有的代码来添加新功能,避免引入新错误
通过遵循开闭原则,可以提高代码的可复用性和稳定性。常用的方法包括使用抽象类、接口以及多态等技术
创建型设计模式:
创建型设计模式关注对象的创建过程,旨在抽象实例化的过程。以下是几种常见的创建型设计模式
工厂方法模式(Factory Method Pattern)
抽象工厂模式(Abstract Factory Pattern)
单例模式(Singleton Pattern)
生成器模式(Builder Pattern)
原型模式(Prototype Pattern)
一、工厂模式
工厂模式,会从如下三个部分进行介绍:
1.简单工厂
简单工厂模式是一种创建型设计模式,用于根据传入的参数决定创建哪种类的实例。虽然它不属于设计模式的“正式”分类,但在实际应用中非常常见
(1)问题产生
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43
| class Car{ public: Car(string name):_name(name){} virtual void show() = 0;
protected: string _name; };
class Bwm:public Car{ public: Bwm(string name): Car(name){} void show() { cout << "获取了一辆宝马汽车" << _name << endl; } };
class Audi:public Car{ public: Audi(string name): Car(name){} void show() { cout << "获取了一辆奥迪汽车" <<_name << endl; } };
int main() { Car *p1 = new Bwm("X1"); Car *p2 = new Audi("A6"); p1->show(); p2->show(); delete p1; delete p2; return 0; }
|
在上述代码存在问题,作为开发者,需要记住产品派生类的名字,进而创建出对应的对象(直接使用具体的产品类创建具体的类的对象),这样理解:
去4S
店买车,我们简单的做法,就是看好车之后,直接问4S
店购买一辆对应的车(宝马、奥迪)。但是根据上述的代码,就相当于用户需要车,还需要自己去车间,亲自制造一辆奥迪、宝马车new
一辆
因此引出后文的简单工厂,其中工厂类就相当于4S
店,调用相关的接口就可以获得一辆需要的车(产品类实例对象)
(2)简单工厂代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84
| class Car{ public: Car(string name):_name(name){} virtual void show() = 0;
protected: string _name; };
class Bmw:public Car{ public: Bmw(string name): Car(name){} void show() { cout << "获取了一辆宝马汽车" << _name << endl; } };
class Audi:public Car{ public: Audi(string name): Car(name){} void show() { cout << "获取了一辆奥迪汽车" <<_name << endl; } };
enum CarType{ BMW, AUDI, };
class SimpleFactory{ public: Car *createCar(CarType ct) { switch (ct) { case BMW: return new Bmw("X1"); break; case AUDI: return new Audi("A6"); break;; default: cerr << "传入工厂的参数不正确: "; break; } return nullptr; } };
int main() { unique_ptr<SimpleFactory> factory(new SimpleFactory());
unique_ptr<Car> p1(factory->createCar(BMW)); unique_ptr<Car> p2(factory->createCar(AUDI));
p1->show(); p2->show();
return 0; }
|
在上述的基础上,使用智能指针,对工厂new
出的类实例对象,不需要用户人为的delete
,可以自动释放资源
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| int main() { unique_ptr<SimpleFactory> factory(new SimpleFactory());
unique_ptr<Car> p1(factory->createCar(BMW)); unique_ptr<Car> p2(factory->createCar(AUDI));
p1->show(); p2->show();
return 0; }
|
在上述代码工厂类SimpleFactory
的createCar
方法中,根据传入的参数决定创建哪种类的实例
结论:将对象的创建封装在一个接口函数里面,通过传入不同的标识,返回创建的对象
(3)优缺点
优点:
简化对象创建:客户端无需直接实例化对象,简化了对象创建过程
集中管理对象创建:对象的创建逻辑集中在工厂类中,便于维护和修改
缺点:
违反开闭原则:每新增一种产品都需要修改工厂类,不符合开闭原则(对扩展开放,修改关闭)
单一职责原则:工厂类承担了过多的职责,容易变得复杂
适用场景:
需要根据条件创建不同类型对象:根据传入的参数决定创建哪种类的实例。
客户端不需要了解产品的具体实现:客户端只需知道如何使用产品接口,无需关心具体实现
2.工厂方法模式
工厂方法模式(Factory Method Pattern
)是一种创建型设计模式,定义了一个用于创建对象的接口,但由子类决定实例化哪个类。这样使得一个类的实例化延迟到其子类
工厂方法,相较于简单工厂,满足了软件设计的 开闭原则
工厂方法相较于简单方法的升级:
划分了一个继承结构,工厂基类Factory
,封装了一个纯虚方法createCar()
即工厂方法
一个工厂的派生类就代表一个具体工厂,一个具体工厂就产生一个具体的产品实例(重写纯虚方法)
符合开闭原则
,若像新增别的具体工厂,不需要修改之前的内容,而是新增一个工厂的派生类对应一个具体的工厂
工厂方法主要角色:
抽象工厂(Creator):声明一个工厂方法(重点:一个),返回一个产品对象。可以调用工厂方法以代替直接实例化产品对象
具体工厂(Concrete Creator):实现工厂方法,返回具体产品实例
抽象产品(Product):定义产品对象的接口
具体产品(Concrete Product):实现产品接口
(1)工厂方法代码示例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81
| class Car{ public: Car(string name):_name(name){} virtual void show() = 0;
protected: string _name; };
class Bmw:public Car{ public: Bmw(string name): Car(name){} void show() { cout << "获取了一辆宝马汽车" << _name << endl; } };
class Audi:public Car{ public: Audi(string name): Car(name){} void show() { cout << "获取了一辆奥迪汽车" <<_name << endl; } };
class Factory{ public: virtual Car* createCar(string name) = 0; };
class BmwFactory:public Factory{ public: Car *createCar(string name) { return new Bmw(name); } };
class AudiFactory:public Factory{ public: Car *createCar(string name) { return new Audi(name); } };
int main() { unique_ptr<Factory> bmwfty(new BmwFactory()); unique_ptr<Factory> audifty(new AudiFactory()); unique_ptr<Car> p1(bmwfty->createCar("X6")); unique_ptr<Car> p2(audifty->createCar("A8")); p1->show(); p2->show(); return 0; }
|
一个抽象工厂派生类就代表一个具体工厂,一个具体工厂有一个工厂方法接口,调用会产生一种具体的产品实例
(2)优缺点
优点:
遵循开闭原则:可以通过引入新产品类和具体工厂类扩展产品,客户端无需修改代码
符合单一职责原则:每个具体工厂类负责创建具体产品的实例
减少耦合:客户端通过抽象工厂接口操作产品,避免了直接依赖具体产品
缺点:
增加代码复杂度:引入多个工厂类和产品类,增加了系统的复杂性
增加类数量:每增加一个产品,需要对应增加一个具体工厂类
使用场景:
需要创建的对象具有复杂结构或过程:工厂方法隐藏了对象创建的复杂性
系统需要独立于对象创建和使用:使得系统的对象创建和使用分离
产品类经常变化:系统可以在不修改客户端代码的情况下引入新产品
重点:实际上,很多产品是有关联的,属于一个产品族,不应该放在不同的具体工厂里面去创建,这样不符合实际产品对象创建逻辑,二是工厂类太多,不好维护,因此引出,抽象工厂
3.抽象工厂模式
将一系列有关联关系的,属于一个产品族的所有产品创建的接口函数,放在一个抽象工厂里面,AbstractFactory
,该抽象工厂的派生类就是这一个产品族的具体工厂,负责创建该产品族里面的所有产品
抽象工厂模式是一种创建型设计模式,提供一个接口,用于创建一系列相关或互相依赖的对象,而无需指定它们的具体类。它通过组合多个工厂方法,实现对产品族的创建
主要角色:
抽象工厂(Abstract Factory):声明一组用于创建各种产品的方法(和工厂方法模式存在区别)
具体工厂(Concrete Factory):实现创建产品的方法,返回具体产品实例
抽象产品(Abstract Product):为每种产品声明接口
具体产品(Concrete Product):实现产品接口
(1)抽象工厂代码示例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134
| class Car{ public: Car(string name):_name(name){} virtual void show() = 0;
protected: string _name; };
class Bmw:public Car{ public: Bmw(string name): Car(name){} void show() { cout << "获取了一辆宝马汽车" << _name << endl; } };
class Audi:public Car{ public: Audi(string name): Car(name){} void show() { cout << "获取了一辆奥迪汽车" <<_name << endl; } };
class Light{ public: Light(){} virtual void show() = 0; };
class BmwLight:public Light{ public: BmwLight(){} void show() override { cout << "BMW Light!" << endl; } };
class AudiLight:public Light{ public: AudiLight(){} void show() override { cout << "AUDI Light!" << endl; } };
class AbstractFactory{ public: virtual Car* createCar(string name) = 0; virtual Light* createCarLight() = 0; };
class BMWFactory:public AbstractFactory{ public: Car* createCar(string name) override { return new Bmw(name); } Light *createCarLight() override { return new BmwLight(); } };
class AUDIFactory:public AbstractFactory{ public: Car* createCar(string name) override { return new Audi(name); } Light *createCarLight() override { return new AudiLight(); } };
int main() { unique_ptr<AbstractFactory> bmwfty(new BMWFactory()); unique_ptr<AbstractFactory> audifty(new AUDIFactory()); unique_ptr<Car> p1(bmwfty->createCar("X6")); unique_ptr<Car> p2(audifty->createCar("A8")); unique_ptr<Light> L1(bmwfty->createCarLight()); unique_ptr<Light> L2(audifty->createCarLight());
p1->show(); L1->show();
p2->show(); L2->show();
return 0; }
|
(2)区别
工厂方法模式与抽象工厂模式的区别:
(3)优缺点
优点:
分离接口和实现:客户端通过抽象工厂和抽象产品接口操作,不依赖于具体产品类
产品族一致性:确保同一工厂生产的产品是相互兼容的
遵循开闭原则:添加新的产品族时,只需新增具体工厂和产品类,无需修改现有代码
缺点:
增加复杂性:系统中涉及多个接口和类,增加了代码复杂度
扩展不便:增加新的产品类型时,需要修改抽象工厂接口及所有具体工厂类
(4)适用场景
产品对象有复杂的依赖关系:需要确保一系列对象一起使用
需要创建一系列相关或依赖的对象:例如跨平台界面库,需在不同平台上创建不同的界面控件
系统不应依赖于产品类实例如何被创建、组合和表达:通过抽象工厂模式隔离具体类