设计模式
设计模式,即Design Patterns,是指在软件设计中,被反复使用的一种代码设计经验。使用设计模式的目的是为了可重用代码,提高代码的可扩展性和可维护性。
设计模式类型:
类型 |
描述 |
创建型模式(CreationalPatterns) |
用于构建对象,以便它们可以从实现系统中分离出来。 |
结构型模式(Structural Patterns) |
用于在许多不同的对象之间形成大型对象结构。 |
行为型模式(BehavioralPatterns) |
用于管理对象之间的算法、关系和职责。 |
设计模式分类:
创建型模式 |
描述 |
单例模式(Singleton Pattern) |
保证一个类仅有一个实例,并提供一个访问它的全局访问点 |
抽象工厂模式(Abstract Factory Pattern) |
提供一个创建一系列相关或相互依赖对象的接口,而无需指定它们具体的类。 |
建造者模式(Builder Pattern) |
将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。 |
工厂方法模式(Factory Method Pattern) |
定义一个用于创建对象的接口,让子类决定将哪一个类实例化。Factory Method 使一个类的实例化延迟到其子类。 |
原型模式(Prototype Pattern) |
用原型实例指定创建对象的种类,并且通过拷贝这个原型来创建新的对象。 |
结构型模式 |
描述 |
适配器模式(Adapter Pattern) |
将一个类的接口转换成客户希望的另外一个接口。Adapter模式使得原本由于接口不兼容而不能一起工作的那些类可以一起工作。 |
桥接模式(Bridge Pattern) |
将抽象部分与它的实现部分分离,使它们都可以独立地变化。 |
装饰者模式(Decorator Pattern) |
动态地给一个对象添加一些额外的职责。就扩展功能而言,它比生成子类方式更为灵活。 |
组合模式(CompositePattern) |
将对象组合成树形结构以表示”部分-整体”的层次结构。它使得客户对单个对象和复合对象的使用具有一致性。 |
外观模式 (Facade Patterm) |
为子系统中的一组接口提供一个一致的界面,Facade 模式定义了一个高层接口,这个接口使得这一子系统更加容易使用。 |
享元模式(Flyweight Pattern) |
运用共享技术有效地支持大量细粒度的对象。 |
代理模式(ProxyPattern) |
为其他对象提供一个代理以控制对这个对象的访问。 |
行为型模式 |
描述 |
模版方法模式(Template Method Pattern) |
定义一个操作中的算法的骨架,而将一些步骤延迟到子类中。Template Method 使得子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤。 |
命令模式(Command Pattern) |
将一个请求封装为一个对象,从而使你可用不同的请求对客户进行参数化;对请求排队或记录请求日志,以及支持可取消的操作。 |
迭代器模式(Iterator Pattern) |
提供一种方法顺序访问一个聚合对象中各个元素,而又不需暴露该对象的内部表示。 |
观察者模式(Observer Pattern) |
定义对象间的一种一对多的依赖关系,以便当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并自动刷新。 |
中介者模式(Mediator Pattern) |
用一个中介对象来封装一系列的对象交互。中介者使各对象不需要显式地相互引用,从而使其耦合松散,而且可以独立地改变它们之间的交互。 |
备忘录模式(Memento Pattern) |
在不破坏封装性的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态。这样以后就可将该对象恢复到保存的状态。 |
解释器模式(Interpreter Pattern) |
给定一个语言,定义它的文法的一种表示,并定义一个解释器,该解释器使用该表示来解释语言中的句子。 |
状态模式(State Pattern) |
允许一个对象在其内部状态改变时改变它的行为。对象看起来似乎修改了它所属的类。 |
策略模式(StrategyPattern) |
定义一系列的算法,把它们一个个封装起来,并且使它们可相互替换。本模式使得算法的变化可独立于使用它的客户端。 |
职责链模式(Chain of Responsibility Pattern) |
为解除请求的发送者和接收者之间耦合,而使多个对象都有机会处理这个请求。将这些对象连成一条链,并沿着这条链传递该请求,直到有一个对象处理它。 |
访问者模式(VisitorPattern) |
表示一个作用于某对象结构中的各元素的操作。它使你可以在不改变各元素的类的前提下定义作用于这些元素的新操作。 |
创建型模式
单例模式(4)
保证一个类仅有一个实例,并提供一个访问它的全局访问点。
参考链接:https://github.com/Light-City/CPlusPlusThings/tree/master/design_pattern/singleton
懒汉模式
第一次用到类实例的时候才会去实例化。
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
| #include <iostream> #include <mutex>
using namespace std;
class singleton { private: singleton(){} static singleton *sin;
public: static singleton *getInstance(); void dosomething(); };
singleton *singleton::sin = nullptr; mutex mtx; singleton *singleton::getInstance() { if (sin == nullptr) { lock_guard<mutex> lmutex(mtx); if (sin == nullptr) { sin = new singleton(); } }
return sin; }
void singleton::dosomething() { cout << "单例模式" << endl; }
int main() { singleton* obj = singleton::getInstance(); obj->dosomething();
return 0; }
|
解决 内存读写乱序执行
基于operator new+placement new,遵循1,2,3执行顺序依次编写代码。
1 2 3 4 5 6 7 8 9 10 11 12
| singleton *instance() { if (p == nullptr) { lock_guard<mutex> guard(lock_); if (p == nullptr) { singleton *tmp = static_cast<singleton *>(operator new(sizeof(singleton))); new(tmp)singleton(); p = tmp; } } return p; }
|
基于直接嵌入ASM汇编指令mfence,uninx的barrier宏也是通过该指令实现的。
1 2 3 4 5 6 7 8 9 10 11
| #define barrier() __asm__ volatile ("lwsync") singleton *singleton::instance() { if (p == nullptr) { lock_guard<mutex> guard(lock_); barrier(); if (p == nullptr) { p = new singleton(); } } return p; }
|
饿汉模式
单例类定义的时候就进行了实例化。
线程安全。
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
| #include <iostream> #include <mutex>
using namespace std;
class singleton { private: singleton(){} static singleton *sin;
public: static singleton *getInstance(); void dosomething(); };
singleton *singleton::sin = new singleton(); singleton *singleton::getInstance() { return sin; }
void singleton::dosomething() { cout << "单例模式" << endl; }
int main() { singleton* obj = singleton::getInstance(); obj->dosomething();
return 0; }
|
其它实现单例模式方式
静态局部变量
- 单线程下,正确。
- C++11及以后的版本(如C++14)的多线程下,正确。
新的C++标准规定了当一个线程正在初始化一个变量的时候,其他线程必须得等到该初始化完成以后才能访问它。
- C++11之前的多线程下,不一定正确。
1 2 3 4 5 6 7 8 9 10 11
| class singleton { private: singleton(){} public: static singleton& getInstance() { static singleton instance; return instance; } };
|
call_once 函数
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| singleton *singleton::sin = nullptr; once_flag flag; singleton *singleton::getInstance() { call_once(flag, []{ if (sin == nullptr) { sin = new singleton(); } });
return sin; }
|
工厂模式
主要是对创建对象进行了一个封装。
参考链接:https://blog.csdn.net/m0_46308273/article/details/117126962
ps: 开闭原则
对扩展开放、对修改关闭;
也就是说,我们可以添加代码,但是添加代码的时候不能够对现有的代码进行修改。
简单工厂模式(4)
简单工厂可以做到,让用户创建对象的时候只需要知道对象的名称(BMW、AUDI)就好,而不需要关心创建对象的细节(BMW是如何建造的、型号是什么等等细节)。
缺点:未遵循开闭原则。即要扩展对象时,需要修改工厂类。
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
| #include <iostream> #include <memory>
using namespace std;
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() override { cout << "宝马:" << name_ << endl; } };
class Audi : public Car { public: Audi(string name) : Car(name) {} void show() override { cout << "奥迪:" << name_ << endl; } };
enum class CarType { BMW, AUDI };
class simpleFactory { public: simpleFactory() = default; Car *createCar(CarType car) { switch (car) { case CarType::BMW: return new Bmw("x6"); case CarType::AUDI: return new Audi("a8"); default: cerr << "参数错误" << endl; } return nullptr; } };
int main() { unique_ptr<simpleFactory> fac(new simpleFactory());
unique_ptr<Car> p1(fac->createCar(CarType::BMW)); unique_ptr<Car> p2(fac->createCar(CarType::AUDI));
p1->show(); p2->show();
return 0; }
|
工厂方法模式(5)
定义一个创建对象的接口,让其子类自己决定实例化哪一个工厂类,工厂模式使其创建过程延迟到子类进行。
工厂方法的思想就是定义一个Factory基类,基类中定义了一个纯虚函数(创建产品);
之后定义派生类(具体产品的工厂)负责创建对应的产品。
可以做到不同的产品在不同的工厂里面创建,能够对现有工厂以及产品的修改关闭。
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
| #include <iostream> #include <memory>
using namespace std;
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() override { cout << "宝马:" << name_ << endl; } };
class Audi : public Car { public: Audi(string name) : Car(name) {} void show() override { cout << "奥迪:" << name_ << endl; } };
enum class CarType { BMW, AUDI };
class Factory { public: virtual Car* createCar(string type) = 0; };
class bmwFactory : public Factory { public: Car* createCar(string type) override { return new Bmw(type); } };
class audiFactory : public Factory { public: Car* createCar(string type) override { return new Audi(type); } };
int main() { unique_ptr<Factory> bmwf(new bmwFactory()); unique_ptr<Factory> audif(new audiFactory());
unique_ptr<Car> p1(bmwf->createCar("x6")); unique_ptr<Car> p2(audif->createCar("a8"));
p1->show(); p2->show();
return 0; }
|
抽象工厂模式(5)
提供一个创建一系列相关或相互依赖对象的接口,而无需指定它们具体的类。
把有关联关系的,属于一个产品簇的所有产品创建的接口函数,放在一个抽象工厂里面AbstractFactory,派生类(具体产品的工厂)应该负责创建该产品簇里面所有的产品。
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
| #include <iostream> #include <memory>
using namespace std;
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() override { cout << "宝马:" << name_ << endl; } };
class Audi : public Car { public: Audi(string name) : Car(name) {} void show() override { cout << "奥迪:" << name_ << endl; } };
class Light { public: virtual void show() const = 0; };
class bmwLight : public Light { public: void show() const override { cout << "Bmw Light." << endl; } };
class audiLight : public Light { public: void show() const override { cout << "Audi Light." << endl; } };
enum class CarType { BMW, AUDI };
class Factory { public: virtual Car* createCar(string type) = 0; virtual Light* createLight() = 0; };
class bmwFactory : public Factory { public: Car* createCar(string type) override { return new Bmw(type); }
Light* createLight() override { return new bmwLight(); } };
class audiFactory : public Factory { public: Car* createCar(string type) override { return new Audi(type); }
Light* createLight() override { return new audiLight(); } };
int main() { unique_ptr<Factory> bmwf(new bmwFactory()); unique_ptr<Factory> audif(new audiFactory());
unique_ptr<Car> p1(bmwf->createCar("x6")); unique_ptr<Car> p2(audif->createCar("a8")); unique_ptr<Light> p3(bmwf->createLight()); unique_ptr<Light> p4(audif->createLight());
p1->show(); p3->show(); p2->show(); p4->show();
return 0; }
|
建造者模式(2)
将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。
通过这种方式,建造者模式允许你改变一个产品的内部表示,而不会影响到构建该产品的过程。
参考链接:https://blog.csdn.net/jj6666djdbbd/article/details/128576583
建造者模式通常包含以下四个核心组成部分:
- 产品(Product):这是最终要创建的复杂对象。在下述代码示例中,Computer类代表了产品,它包含了构成复杂对象的各个部分,如显示器(display)和主机(host)。
- 抽象建造者(Abstract Builder):这是一个包含创建产品各个部分的声明方法的接口或抽象类。在示例中,abstractBuilder类定义了构建Computer对象所需的步骤,如buildDisplay和buildHost。
- 具体建造者(Concrete Builder):实现了抽象建造者接口的类,并提供了完成构建过程的具体逻辑。在示例中,computerBuilder类是具体建造者,它实现了如何构建Computer的具体部件。
- 指挥者(Director):负责安排已有模块的构建顺序,然后向用户提供产品。在示例中,Director类接受一个abstractBuilder对象,并通过调用其构建方法来组装Computer对象。指挥者知道建造者接口,并且构建一个复杂对象的步骤定义在指挥者类中。
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
| #include <iostream> #include <vector> #include <memory>
using namespace std;
class abstractProduct { public: virtual ~abstractProduct() {} virtual void setDisplay(string display) = 0; virtual void setHost(string host) = 0; virtual void show() const = 0; };
class Computer : public abstractProduct { public: ~Computer() {} void setDisplay(string display) { vec_.emplace_back(display); } void setHost(string host) { vec_.emplace_back(host); } void show() const { for (auto &x : vec_) { cout << x << " " << endl; } }
private: vector<string> vec_; };
class abstractBuilder { public: abstractBuilder():product(new Computer){} ~abstractBuilder() {} virtual void buildDisplay(string display) = 0; virtual void buildHost(string host) = 0; abstractProduct* getProduct() { return product; }
protected: abstractProduct* product; };
class computerBuilder : public abstractBuilder { public: ~computerBuilder(){} void buildDisplay(string display) override { product->setDisplay(display); } void buildHost(string host) override { product->setHost(host); } };
class Director { public: Director(abstractBuilder* builder): m_builder(builder){} ~Director() {} abstractProduct* createComputer(string display, string host) { m_builder->buildDisplay(display); m_builder->buildHost(host); return m_builder->getProduct(); }
private: abstractBuilder* m_builder; };
int main() { abstractBuilder* abBuilder = new computerBuilder(); Director* director = new Director(abBuilder); abstractProduct* obj = director->createComputer("goodDisplay", "goodHost");
obj->show();
delete abBuilder; delete director; delete obj;
return 0; }
|
原型模式(3)
用原型实例指定创建对象的种类,并且通过拷贝这个原型来创建新的对象。
参考链接:https://blog.csdn.net/jj6666djdbbd/article/details/128621283
使用场景:某些结构复杂的对象的创建工作中由于需求的变化,这些对象经常面临着剧烈的变化,但是他们却拥有比较稳定一致的接口。此时便可以使用原型模式。
实现步骤:
- 提供一个抽象原型类:规定了具体原型对象必须实现的接口。
- 提供多个具体原型类:实现抽象原型类的
clone()
方法,它是可被复制的对象。
- 提供访问类:使用具体原型类中的
clone()
方法来复制新的对象。
示例代码:
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 135 136 137 138 139 140 141 142 143 144 145
| #include <iostream> #include <string> #include <unordered_map>
using namespace std;
template <typename K, typename V> using umap = unordered_map<K, V>;
class Shape { public: virtual ~Shape(){} virtual Shape* clone() = 0; virtual int getId() = 0; virtual string getType() = 0;
protected: string type_;
private: int id_; };
class Circle : public Shape { public: Circle(string type, int id):type_(type), id_(id) {} ~Circle() {} Circle(const Circle& lhs) { type_ = lhs.type_; id_ = lhs.id_; } Shape* clone() override { return new Circle(*this); }
int getId() override { return id_; }
string getType() override { return type_; }
protected: string type_;
private: int id_; };
class Square : public Shape { public: Square(string type, int id):type_(type), id_(id) {} ~Square() {} Square(const Square& lhs) { type_ = lhs.type_; id_ = lhs.id_; } Shape* clone() override { return new Square(*this); }
int getId() override { return id_; }
string getType() override { return type_; }
protected: string type_;
private: int id_; };
class ShapeType { public: ~ShapeType() { for (auto& x : shapeMap_) { delete x.second; x.second = nullptr; } } ShapeType() { Circle* circle(new Circle("circle", 1)); Square* square(new Square("square", 2)); shapeMap_.emplace(circle->getType(), circle); shapeMap_.emplace(square->getType(), square); }
Shape* getShape(string type) { return shapeMap_[type]->clone(); }
private: umap<string, Shape*> shapeMap_; };
int main() { ShapeType* st = new ShapeType();
Shape* circle = st->getShape("circle"); Shape* square = st->getShape("square");
cout << circle->getId() << " " << circle->getType() << endl; cout << square->getId() << " " << square->getType() << endl;
delete st; delete circle; delete square;
return 0; }
|
结构型模式
用于在许多不同的对象之间形成大型对象结构。
适配器模式(4)
将一个类的接口转换成客户希望的另外一个接口。Adapter模式使得原本由于接口不兼容而不能一起工作的那些类可以一起工作。
面对客户端的话,我们需要将原有类的接口转换为客户端所需要的接口,也就是定义一个适配器类将原有类进行包装,通过适配器类关联原有类,从而在适配器类内部调用原有类的接口,并向外提供给客户端所需的接口。
参考链接:https://blog.csdn.net/yxh_1_/article/details/116085949
适配器应用场景
新旧 API 兼容。(大型使用库的升级)
三方包适配。
统一多个类的接口。(比如数据库ORM)
示例代码:
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
| #include <iostream>
using namespace std;
enum class sqlType { MYSQL = 1, SQLITE, POSTGERSQL }; sqlType type;
class Mysql { public: void select() { cout << "mysql selected!" << endl; } };
class Sqlite { public: void select() { cout << "sqlite selected!" << endl; } };
class Postgersql { public: void select() { cout << "postgersql selected!" << endl; } };
class ORM : public Mysql, public Sqlite, public Postgersql { public: void orm_select() { switch (type) { case sqlType::MYSQL: Mysql::select(); break; case sqlType::SQLITE: Sqlite::select(); break; case sqlType::POSTGERSQL: Postgersql::select(); break; default: break; } } };
void init_sqltype(sqlType t) { type = t; }
int main() { ORM* obj = new ORM();
init_sqltype(sqlType::MYSQL); obj->orm_select();
init_sqltype(sqlType::SQLITE); obj->orm_select();
init_sqltype(sqlType::POSTGERSQL); obj->orm_select();
delete obj; return 0; }
|