设计模式分类
设计模式可以分为三种类型:创建型设计模式、结构型设计模式和行为型设计模式。
创建型设计模式:这些模式涉及到对象的创建机制,包括简单工厂模式、工厂方法模式、抽象工厂模式、单例模式、建造者模式和原型模式。
结构型设计模式:这些模式涉及到类和对象的组合,包括适配器模式、桥接模式、组合模式、装饰器模式、外观模式、享元模式和代理模式。
行为型设计模式:这些模式涉及到对象之间的通信和交互,包括责任链模式、命令模式、解释器模式、迭代器模式、中介者模式、备忘录模式、观察者模式、状态模式、策略模式、模板方法模式和访问者模式。
本文是对创建型设计模式中的单例、工厂模式的一个总结。
单例模式
定义
保证一个类仅有一个实例,并提供一个该实例的全局访问点。
示例:(懒汉模式-非线程安全)
class Singleton { public: static Singleton * GetInstance() { if (_instance == nullptr) { _instance = new Singleton(); } return _instance; } private: Singleton(){}; //构造 ~Singleton(){}; Singleton(const Singleton &) = delete; //拷⻉ 构造 Singleton& operator=(const Singleton&) = delete;//拷贝赋值构造 Singleton(Singleton &&) = delete;//移动构造 Singleton& operator=(Singleton &&) = delete;//移动拷贝构造 static Singleton * _instance; }; Singleton* Singleton::_instance = nullptr;//静态成员需要初始化
懒汉模式与饿汉模式
直观区别:饿汉:饿汉就是类一旦加载,就把单例初始化完成,保证getInstance的时候,单例是已经存在了。懒汉:懒汉比较懒,只有当调用getInstance的时候,才回去初始化这个单例。
从性能线程上的区别:
1、线程安全:饿汉式天生就是线程安全的,可以直接用于多线程而不会出现问题。懒汉式本身是非线程安全的。
2、资源加载和性能:饿汉式在类创建的同时就实例化一个静态对象出来,不管之后会不会使用这个单例,都会占据一定的内 存,但是相应的,在第一次调用时速度也会更快,因为其资源已经初始化完成。
而懒汉式顾名思义,会延迟加载,在第一次使用该单例的时候才会实例化对象出来,第一次调用时要做初始化,如果要做的工作比较多,性能上会有些延迟,之后就和饿汉式一样了。
如果这个创建过程很耗时,比如需要连接10000次数据库(夸张了…😃),并且这个类还并不一定会被使用,那么这个创建过程就是无用的。这样的话可能懒汉模式更适合。
饿汉模式示例
#include <iostream> using namespace std; class Singleton { public: static Singleton* instance = new Singleton();//提前实例化 static Singleton* GetInstance() { return instance; } private: Singleton(){}; //构造 ~Singleton(){}; Singleton(const Singleton &) = delete; //拷⻉ 构造 Singleton& operator=(const Singleton&) = delete;//拷贝赋值构造 Singleton(Singleton &&) = delete;//移动构造 Singleton& operator=(Singleton &&) = delete;//移动拷贝构造 }; int main() { Singleton* test1 = Singleton::GetInstance(); cout << test1 << endl; Singleton* test2 = Singleton::GetInstance(); cout << test2 << endl; return 0; }
懒汉模式-线程安全
#include <mutex> class Singleton { // 懒汉模式 lazy load public: static Singleton * GetInstance() { //在外面加锁会造成锁资源浪费,,每次调用都会调用锁。 //我们其实只需要第一次调用的时候才需要锁,其他时候是不需要的 if (_instance == nullptr) { std::lock_guard<std::mutex> lock(_mutex); // 双重检测可以避免第一次读的时候多个线程进入的问题。 if (_instance == nullptr) { _instance = new Singleton(); } } return _instance; } private: static void Destructor() { if (nullptr != _instance) { delete _instance; _instance = nullptr; } } Singleton(){}; //构造 ~Singleton(){}; Singleton(const Singleton &) = delete; //拷⻉构造 Singleton& operator=(const Singleton&) = delete;//拷贝赋值构造 Singleton(Singleton &&) = delete;//移动构造 Singleton& operator=(Singleton &&) = delete;//移动拷贝构造 static Singleton * _instance; static std::mutex _mutex; }; Singleton* Singleton::_instance = nullptr;//静态成员需要初始化 std::mutex Singleton::_mutex; //互斥锁初始化
- 双重if判断可以节约资源。不用双重if的话需要在外层加锁,影响效率。
为何要用到静态成员?
当我们正常书写一个类,我们可以通过这个类创建出很多对象,这显然是不符合单例模式规则的。无论我们是在栈上还是堆区创建对象,都会调用构造函数,如果将构造函数私有化,类外就不能创建对象了。但是随之带来了新的问题,现在的类一个对象都创建不了了。
而静态成员变量可以通过类名的方式访问到,不一定通过创建对象。所以我们在类内public作用域下声明一个静态成员变量,类外是可以访问到的。而且静态成员变量是共享的,才符合单例设计模式。
工厂模式
开闭原则
开闭原则,在面向对象编程领域中,规定“软件中的对象(类,模块,函数等等)应该对于扩展是开放的,但是对于修改是封闭的”,这意味着一个实体是允许在不改变它的源代码的前提下变更它的行为。当软件需要变化时,尽量通过扩展软件实体的行为来实现变化,而不是通过修改已有的代码来实现变化。
定义
工厂模式中实例的创建是通过封装了的工厂方法实现的,而不是常见的new操作来实现的。在工厂模式中,我们创建对象时不会对上层暴露创建逻辑,而是通过使用一个共同结构来指向新创建的对象。
简单工厂模式
每类产品需要有一个虚基类,通过接收类别参数生产具体的产品。
#include <iostream> enum ProductType { PRODUCT_A, PRODUCT_B }; // 产品基类 class Product { public: virtual void Show() = 0; }; // 产品 A class ProductA : public Product { public: void Show() { std::cout << "Product A." << std::endl; } }; // 产品 B class ProductB : public Product { public: void Show() { std::cout << "Porduct B." << std::endl; } }; // 工厂 class Factory { public: Product* Create(int type) { switch(type) { case PRODUCT_A : return new ProductA; break; case PRODUCT_B : return new ProductB; break; default : break; } } }; int main() { Factory *factory = new Factory(); factory->Create(PRODUCT_A)->Show(); factory->Create(PRODUCT_B)->Show(); return 0; }
缺点:简单工厂模式将所有的创建逻辑集中在一个工厂类中。因此需要增加新的类型产品的话,除了增加继承类,还需要修改Factory类中的内容,违背了开闭原则。
工厂方法模式
与简单工厂类相比,通过虚基工厂类来添加新的类型产品。
#include <iostream> // 产品基类 class Product { public: virtual void Show() = 0; }; // 工厂基类 class Factory { public: virtual Product* Create() = 0; }; // 产品 A class ProductA : public Product { public: void Show() { std::cout << "Product A." << std::endl; } }; // 产品 B class ProductB : public Product { public: void Show() { std::cout << "Porduct B." << std::endl; } }; // 工厂 A class FactoryA : public Factory { public: Product* Create() { return new ProductA; } }; // 工厂 B class FactoryB : public Factory { public: Product* Create() { return new ProductB; } }; int main() { FactoryA *factoryA = new FactoryA(); FactoryB *factoryB = new FactoryB(); factoryA->Create()->Show(); factoryB->Create()->Show(); return 0; }
需要新增类型产品时,新增我们的产品继承类,和工厂继承类就行了,符合开闭原则。
缺点:每类产品都需要新建一个工厂类。导致系统过大。
抽象工厂模式
相对于工厂方法模式中需要每类产品都需要新建一个工厂类,抽象工厂模式,是把几类产品组成一个产品组,并在一个工厂类中去做这个产品组中所有产品的构建动作。
#include <iostream> // Product A class ProductA { public: virtual void Show() = 0; }; class ProductA1 : public ProductA { public: void Show() { std::cout << "Product A1." << std::endl ; } }; class ProductA2 : public ProductA { public: void Show() { std::cout << "Product A2." << std::endl ; } }; // Product B class ProductB { public: virtual void Show() = 0; }; class ProductB1 : public ProductB { public: void Show() { std::cout << "Product B1." << std::endl ; } }; class ProductB2 : public ProductB { public: void Show() { std::cout << "Product B2." << std::endl ; } }; // Factory class Factory { public: virtual ProductA* CreateProductA() = 0; virtual ProductB* CreateProductB() = 0; }; class Factory1 : public Factory { public: ProductA* CreateProductA() { return new ProductA1(); } ProductB* CreateProductB() { return new ProductB1(); } }; class Factory2 : public Factory { ProductA* CreateProductA() { return new ProductA2(); } ProductB* CreateProductB() { return new ProductB2(); } }; int main() { Factory *factoryObj1 = new Factory1(); ProductA *productObjA1 = factoryObj1->CreateProductA(); ProductB *productObjB1 = factoryObj1->CreateProductB(); productObjA1->Show(); productObjB1->Show(); Factory *factoryObj2 = new Factory2(); ProductA *productObjA2 = factoryObj2->CreateProductA(); ProductB *productObjB2 = factoryObj2->CreateProductB(); productObjA2->Show(); productObjB2->Show(); return 0; }
缺点:如果要改我们产品组的结构的话,就需要修改我们的工厂类,这一方面就不符合开闭原则了。但在实际业务中,要避免已经确定的产品组不要再改变。
工厂模式的退化过程
当抽象工厂模式中每一个具体工厂类只创建一个产品对象,抽象工厂模式退化成工厂方法模式;当工厂方法模式中抽象工厂与具体工厂合并,提供一个统一的工厂来创建产品对象,并将创建对象的工厂方法设计为静态方法时(非虚类),工厂方法模式退化成简单工厂模式。