1.概要
设计模式是一套被反复使用的代码设计经验的总结,是经过提炼的出色设计方法。设计模式主要是为了解决某类重复出现的问题而出现的一套成功有效的解决方案,设计模式的出现提高代码复用性、扩展性、可维护性、灵活性、稳健性以及安全可靠性的解决方案。
2.分类
创建型模式、结构型模式、行为型模式
3.单例模式
属于创建型模式,三个要点:
1.当前类最多只能创建一个实例
2.当前这个唯一的实例,必须由当前类创建(自主创建),而不是调用者创建
3.必须向整个系统提供全局的访问点来获取唯一的实例
为什么有一些类需要是单例?
以任务管理器为例,它采用的就是单例模式,我们即使多次点开也只会显示一个窗口,如果我们不采用单例模式,我们点击一下就弹出一个窗口,那么我们应该看哪个窗口呢?如果点击的不同窗口内容都一样,那么就需要同步,这显然是不必要的。
单例模式常见于任务管理器、打印机任务队列、线程池、日志、管理者等。
使用单例有什么好处?
1.一个类只有一个对象,避免在不知情的情况下创建对象(类型转换)
2.提供全局的调用点,用起来方便
3.提供延迟初始化,可以避开在程序刚运行时初始化(懒汉式)
4.线程安全(使用锁机制等)
如何实现?
1.将构造、析构、拷贝构造、operator=私有化
2.提供静态公有的获取对象的方法
3.1懒汉式
懒汉式的方法是直到使用时才实例化对象,也就说直到调用Instance() 方法的时候才 new 一个单例的对象, 如果不被调用就不会占用内存。是一种“时间换空间”的做法。如果单线程没有问题,当多线程的时候就会出现不可靠的情况。
#include<iostream>
using namespace std;
//if判断:保证创建唯一实例
//构造函数私有化:防止调用者自己在类外创建实例
//static类成员函数:提供接口
class CSingleton {
CSingleton() = default;//C++11 使用默认的构造
~CSingleton() = default;
CSingleton(const CSingleton&) = delete;//删除拷贝构造(禁用)
CSingleton& operator = (const CSingleton&) = delete;
static CSingleton* m_pSin;
public:
//有问题的代码
static CSingleton* GetSingleton() {
//加锁
if (!m_pSin)
{
m_pSin = new CSingleton;
}
//解锁
return m_pSin;
}
};
CSingleton* CSingleton::m_pSin = nullptr;
int main()
{
CSingleton* m_pSin1 = CSingleton::GetSingleton();
CSingleton* m_pSin2 = CSingleton::GetSingleton();
cout << m_pSin1 << " " << m_pSin2 << endl;//00B49750 00B49750
}
以上代码存在的问题:
1.线程安全的问题:当多线程获取单例时有可能引发竞态条件:第一个线程在if中判断m_pSin是空的,于是开始实例化单例;同时第二个线程也尝试获取单例,这个时候判断m_pSin还是空的,于是也开始实例化单例;这样就会实例化出两个对象,这就是线程安全问题的由来。
解决办法:
1.加锁
#include<iostream>
#include<mutex>
using namespace std;
class CSingleton {
CSingleton() = default;//C++11 使用默认的构造
~CSingleton() = default;
CSingleton(const CSingleton&) = delete;//删除拷贝构造(禁用)
CSingleton& operator = (const CSingleton&) = delete;
static CSingleton* m_pSin;
static mutex m_mutex;
public:
static CSingleton* GetSingleton() {
//使用锁确保线程安全
lock_guard<mutex>lck(m_mutex);
if (!m_pSin)
{
m_pSin = new CSingleton;
}
return m_pSin;
}
};
CSingleton* CSingleton::m_pSin = nullptr;
mutex CSingleton::m_mutex;
int main()
{
CSingleton* m_pSin1 = CSingleton::GetSingleton();
CSingleton* m_pSin2 = CSingleton::GetSingleton();
cout << m_pSin1 << " " << m_pSin2 << endl;//0xa575f0 0xa575f0
}
加锁以后,保证了线程安全,但每次判断m_pSin是否存在时都需要等待锁释放,如何解决这个问题?
使用双层判断
static CSingleton* GetSingleton() {
//使用锁+双层判断
if(!m_pSin)
{
lock_guard<mutex>lck(m_mutex);
if (!m_pSin)
{
m_pSin = new CSingleton;
}
}
return m_pSin;
}
2.使用call_once()
#include<iostream>
#include<mutex>
using namespace std;
class CSingleton {
CSingleton() = default;//C++11 使用默认的构造
~CSingleton() = default;
CSingleton(const CSingleton&) = delete;//删除拷贝构造(禁用)
CSingleton& operator = (const CSingleton&) = delete;
static CSingleton* m_pSin;
static once_flag flag;
public:
static CSingleton* GetSingleton() {
//使用call_once()确保线程安全
if (!m_pSin)
{
//m_pSin = new CSingleton;
call_once(flag,[](){CSingleton::m_pSin = new CSingleton;});
}
return m_pSin;
}
};
CSingleton* CSingleton::m_pSin = nullptr;
once_flag CSingleton::flag;
int main()
{
CSingleton* m_pSin1 = CSingleton::GetSingleton();
CSingleton* m_pSin2 = CSingleton::GetSingleton();
cout << m_pSin1 << " " << m_pSin2 << endl;//0x274777f0 0x274777f0
}
3.静态局部变量
多线程下不会失效,编译器创建静态局部变量保证其原子性。(第一次调用GetSingleton()时创建sin对象,在后续调用该函数时不会继续创建)
public:
static CSingleton* GetSingleton() {
static CSingleton sin;
return &sin;
}
};
2.内存泄漏:注意到类中只负责new出对象,却没有负责delete对象,因此只有构造函数被调用,析构函数却没有被调用,因此会导致内存泄漏。
解决办法:
1.使用自定义函数对内存进行回收,在我们需要回收时进行调用
public:
static void DestorySingleton(CSingleton*& p) {/*这里相当于对指针的引用,如果直接是CSingleton* p,那么对于p的修改将不会影响到外面的m_pSin1与m_pSin2*/
if (p) {
delete p;
p = nullptr;
m_pSin = nullptr;
}
}
2.利用静态类对象在程序结束时自动回收,不需要我们手动回收,从而实现对内存的自动回收
public:
static class DeleteSingleton {
~DeleteSingleton() {
if (m_pSin) {
delete m_pSin;
}
m_pSin = nullptr;
}
}del;
CSingleton::DeleteSingleton CSingleton::del;
3.2饿汉式
在程序创建之初自动创建了对象,它是一个全局的对象,生命周期直到程序结束,所以不需要提供销毁对象的方法。是一种“空间换时间”的做法。不存在多线程下的安全问题。
#include<iostream>
using namespace std;
class CSingleton {
CSingleton(){}
CSingleton(const CSingleton&) = delete;
CSingleton& operator = (const CSingleton&) = delete;
~CSingleton() {
cout << "调用析构" << endl;
}
static CSingleton sin;//静态对象,在编译期就创建好
public:
static CSingleton* GetSingleton() {
return &sin;
}
};
CSingleton CSingleton::sin;
int main()
{
CSingleton* m_pSin1 = CSingleton::GetSingleton();
CSingleton* m_pSin2 = CSingleton::GetSingleton();
cout << m_pSin1 << " " << m_pSin2 << endl;//0009E138 0009E138 调用析构
}
3.3C++11实现单例模式
#include<iostream>
#include<mutex>
#include<memory>
using namespace std;
class CSingleton {
CSingleton() = default;
CSingleton(const CSingleton&) = delete;
CSingleton& operator = (const CSingleton&) = delete;
//智能指针
static unique_ptr<CSingleton>m_pSin;
static mutex m_mutex;
public:
~CSingleton() = default;//使用智能指针管理回收,析构应该变成公有,否则无法调用
static CSingleton* GetSingleton() {
//使用锁+双层判断
//在进入锁之前,先检查变量是否已经初始化,如果已经初始化,直接返回,避免不必要的锁操作。
if(!m_pSin.get())
{
lock_guard<mutex>lck(m_mutex);
if (!m_pSin.get())
{
m_pSin.reset(new CSingleton);
}
}
return m_pSin.get();
}
};
unique_ptr<CSingleton>CSingleton::m_pSin;
mutex CSingleton::m_mutex;
int main()
{
CSingleton* m_pSin1 = CSingleton::GetSingleton();
CSingleton* m_pSin2 = CSingleton::GetSingleton();
cout << m_pSin1 << " " << m_pSin2 << endl;0xaa275f0 0xaa275f0
}
定义销毁器解决智能指针实现单例析构私有无法访问:
先看一下仿函数:
模仿着写一下:
4.工厂模式
属于创建型模式,主要用来集中创建对象的,如果在任何使用的地方创建对象那就造成了类或方法之间的耦合,如果要更换对象那么在所有使用到的地方都要修改一遍,不利于后期的维护,也违背了开闭设计原则,如果使用工厂来创建对象,那么就彻底解耦合了,如果要修改只需要修改工厂类即可。
开闭原则是面向对象设计的一个重要原则,开闭原则的核心思想是:
开于扩展:软件实体(类、模块、函数等)应该对扩展开放,意味着当需要增加新功能时,应该能够添加新的代码来扩展现有的代码,而不是修改现有的代码。
闭于修改:软件实体应该对修改关闭,意味着在添加新功能时,不应该修改现有的代码。这样可以保持现有的代码稳定,减少引入新错误的风险。
工厂模式最大的优势:解耦
#include<iostream>
using namespace std;
class CEngine {
public:
virtual void working() = 0;
};
class CEngine2L :public CEngine {
public:
void working() {
cout << "2.0L发动机正在工作" << endl;
}
};
class CEngine2T :public CEngine {
public:
void working() {
cout << "2.0T发动机正在工作" << endl;
}
};
class CCar {
public:
CEngine* m_pEngine;
CCar() :m_pEngine(new CEngine2L) {}
CCar(const string& type) :m_pEngine(type == "2.0L" ? new CEngine2L : new CEngine2L) {}
void drive() {
if (m_pEngine) {
m_pEngine->working();
cout << "汽车行驶中" << endl;
}
}
~CCar() {
if (m_pEngine)
delete m_pEngine;
m_pEngine = nullptr;
}
};
int main()
{
CCar bmw("2.0L");
bmw.drive();//2.0L发动机正在工作 汽车行驶中
}
我们观察上述代码,如果我们想将CEngine2L修改成有参构造,这是不仅要对CEngine2L类进行修改,后续所有用到CEngine2L类方法的地方我们都要进行相应的修改,这种不但造成了类之间的耦合,还违背了开闭设计原则,为了解决这一问题,我们引入工厂模式。
4.1简单工厂
#include<iostream>
using namespace std;
class CEngine {
public:
virtual void working() = 0;
};
class CEngine2L :public CEngine {
public:
void working() {
cout << "2.0L发动机正在工作" << endl;
}
};
class CEngine2T :public CEngine {
public:
void working() {
cout << "2.0T发动机正在工作" << endl;
}
};
//简单工厂
class CFactoryEngine {
public:
CEngine* CreateEngine(const string& type) {
if (type == "2.0L") {
return new CEngine2L;
}
else if (type == "2.0T") {
return new CEngine2T;
}
else {
return nullptr;
}
}
};
class CCar {
public:
CEngine* m_pEngine;
//CCar() :m_pEngine(new CEngine2L) {}
//CCar(const string& type) :m_pEngine(type == "2.0L" ? new CEngine2L : new CEngine2L) {}
//引入简单工厂后的写法
CCar(CFactoryEngine* pFac, const string& type) :m_pEngine(pFac ? pFac->CreateEngine(type) : nullptr) {};
void drive() {
if (m_pEngine) {
m_pEngine->working();
cout << "汽车行驶中" << endl;
}
}
~CCar() {
if (m_pEngine)
delete m_pEngine;
m_pEngine = nullptr;
}
};
int main()
{
CFactoryEngine Fac;
CCar bmw(&Fac, "2.0L");
bmw.drive();//2.0L发动机正在工作 汽车行驶中
CCar benchi(&Fac, "2.0T");
benchi.drive();//2.0T发动机正在工作 汽车行驶中
}
上例中createEngine方法包含了所有类型发动机(CEngine2L,CEngine2T)的创建,当增加新的产品种类时,除了要增加对应的类外,还要修改工厂的集中创建方法,违背了开闭原则,所以简单工厂适合创建种类比较少且比较固定的对象,对于复杂的业务环境就不太适应了。
4.2工厂方法
由于简单工厂存在着弊端(违背开闭原则),所以在其基础上,进一步将工厂类进行抽象,拆分多个类型的工厂,每个工厂创建对应类型的类对象。
#include<iostream>
using namespace std;
class CEngine {
public:
virtual void working() = 0;
};
class CEngine2L :public CEngine {
public:
void working() {
cout << "2.0L发动机正在工作" << endl;
}
};
class CEngine2T :public CEngine {
public:
void working() {
cout << "2.0T发动机正在工作" << endl;
}
};
//工厂方法
class CFactoryEngine {
public:
virtual CEngine* CreateEngine() = 0;
};
class CFactoryEngine2L :public CFactoryEngine {
public:
virtual CEngine* CreateEngine() {
return new CEngine2L;
}
};
class CFactoryEngine2T :public CFactoryEngine {
public:
virtual CEngine* CreateEngine() {
return new CEngine2T;
}
};
class CCar {
public:
CEngine* m_pEngine;
//CCar() :m_pEngine(new CEngine2L) {}
//CCar(const string& type) :m_pEngine(type == "2.0L" ? new CEngine2L : new CEngine2L) {}
//引入简单工厂后的写法
//CCar(CFactoryEngine* pFac, const string& type) :m_pEngine(pFac ? pFac->CreateEngine(type) : nullptr) {};
//引入工厂方法后的写法
CCar(CFactoryEngine* pFac) :m_pEngine(pFac ? pFac->CreateEngine() : nullptr) {};
void drive() {
if (m_pEngine) {
m_pEngine->working();
cout << "汽车行驶中" << endl;
}
}
~CCar() {
if (m_pEngine)
delete m_pEngine;
m_pEngine = nullptr;
}
};
int main()
{
CFactoryEngine* pFac2L = new CFactoryEngine2L;
CFactoryEngine* pFac2T = new CFactoryEngine2T;
CCar bmw(pFac2L);
bmw.drive();//2.0L发动机正在工作 汽车行驶中
CCar benchi(pFac2T);
benchi.drive();//2.0T发动机正在工作 汽车行驶中
}
工厂方法模式每个子类工厂对应一个具体类型的对象,比如CFactoryEngine2L对应CEngine2L对象,遵循了开闭原则,提高了扩展性(如果增加了新的类型的发动机那么原有的发动机工厂不用改动),但如果对象种类较多,那么每一个都要对应一个工厂,需要大量的工厂得不偿失。
举例:我们新增加一个变速箱类
#include<iostream>
using namespace std;
class CEngine {
public:
virtual void working() = 0;
};
class CEngine2L :public CEngine {
public:
void working() {
cout << "2.0L发动机正在工作" << endl;
}
};
class CEngine2T :public CEngine {
public:
void working() {
cout << "2.0T发动机正在工作" << endl;
}
};
class CGearBox {
public:
virtual void working() = 0;
};
class CGearBoxAuto :public CGearBox {
public:
void working() {
cout << "自动挡变速箱正在工作" << endl;
}
};
class CGearBoxManual :public CGearBox {
public:
void working() {
cout << "手动挡变速箱正在工作" << endl;
}
};
//工厂方法
class CFactoryEngine {
public:
virtual CEngine* CreateEngine() = 0;
};
class CFactoryEngine2L :public CFactoryEngine {
public:
virtual CEngine* CreateEngine() {
return new CEngine2L;
}
};
class CFactoryEngine2T :public CFactoryEngine {
public:
virtual CEngine* CreateEngine() {
return new CEngine2T;
}
};
class CFactoryGearBox {
public:
virtual CGearBox* CreateGearBox() = 0;
};
class CFactoryGearBoxAuto :public CFactoryGearBox {
public:
virtual CGearBox* CreateGearBox() {
return new CGearBoxAuto;
}
};
class CFactoryGearBoxManual :public CFactoryGearBox {
public:
virtual CGearBox* CreateGearBox() {
return new CGearBoxManual;
}
};
class CCar {
public:
CEngine* m_pEngine;
CGearBox* m_pGearBox;
//CCar() :m_pEngine(new CEngine2L) {}
//CCar(const string& type) :m_pEngine(type == "2.0L" ? new CEngine2L : new CEngine2L) {}
//引入简单工厂后的写法
//CCar(CFactoryEngine* pFac, const string& type) :m_pEngine(pFac ? pFac->CreateEngine(type) : nullptr) {};
//引入工厂方法后的写法
CCar(CFactoryEngine* pFacEngine, CFactoryGearBox* pFacGearBox) :
m_pEngine(pFacEngine ? pFacEngine->CreateEngine() : nullptr),
m_pGearBox(pFacGearBox ? pFacGearBox->CreateGearBox() : nullptr) {};
void drive() {
if (m_pEngine && m_pGearBox) {
m_pEngine->working();
m_pGearBox->working();
cout << "汽车行驶中" << endl;
}
}
~CCar() {
if (m_pEngine)
delete m_pEngine;
m_pEngine = nullptr;
if (m_pGearBox)
delete m_pGearBox;
m_pGearBox = nullptr;
}
};
int main()
{
CFactoryEngine* pFac2L = new CFactoryEngine2L;
CFactoryGearBox* pFacAuto = new CFactoryGearBoxAuto;
CCar bmw(pFac2L, pFacAuto);
bmw.drive(); //2.0L发动机正在工作 自动挡变速箱正在工作 汽车行驶中
}
4.3抽象工厂
#include<iostream>
using namespace std;
class CEngine {
public:
virtual void working() = 0;
};
class CEngine2L :public CEngine {
public:
void working() {
cout << "2.0L发动机正在工作" << endl;
}
};
class CEngine2T :public CEngine {
public:
void working() {
cout << "2.0T发动机正在工作" << endl;
}
};
class CGearBox {
public:
virtual void working() = 0;
};
class CGearBoxAuto :public CGearBox {
public:
void working() {
cout << "自动挡变速箱正在工作" << endl;
}
};
class CGearBoxManual :public CGearBox {
public:
void working() {
cout << "手动挡变速箱正在工作" << endl;
}
};
//工厂方法
//class CFactoryEngine {
//public:
// virtual CEngine* CreateEngine() = 0;
//};
//
//class CFactoryEngine2L :public CFactoryEngine {
//public:
// virtual CEngine* CreateEngine() {
// return new CEngine2L;
// }
//};
//
//class CFactoryEngine2T :public CFactoryEngine {
//public:
// virtual CEngine* CreateEngine() {
// return new CEngine2T;
// }
//};
//
//class CFactoryGearBox {
//public:
// virtual CGearBox* CreateGearBox() = 0;
//};
//
//class CFactoryGearBoxAuto :public CFactoryGearBox {
//public:
// virtual CGearBox* CreateGearBox() {
// return new CGearBoxAuto;
// }
//};
//
//class CFactoryGearBoxManual :public CFactoryGearBox {
//public:
// virtual CGearBox* CreateGearBox() {
// return new CGearBoxManual;
// }
//};
//抽象工厂
class CFactory {
public:
virtual CEngine* CreateEngine() = 0;
virtual CGearBox* CreateGearBox() = 0;
};
class CFac2LAuto :public CFactory {
public:
virtual CEngine* CreateEngine() {
return new CEngine2L;
}
virtual CGearBox* CreateGearBox() {
return new CGearBoxAuto;
}
};
class CCar {
public:
CEngine* m_pEngine;
CGearBox* m_pGearBox;
//CCar() :m_pEngine(new CEngine2L) {}
//CCar(const string& type) :m_pEngine(type == "2.0L" ? new CEngine2L : new CEngine2L) {}
//引入简单工厂后的写法
//CCar(CFactoryEngine* pFac, const string& type) :m_pEngine(pFac ? pFac->CreateEngine(type) : nullptr) {};
//引入工厂方法后的写法
/*CCar(CFactoryEngine* pFacEngine, CFactoryGearBox* pFacGearBox) :
m_pEngine(pFacEngine ? pFacEngine->CreateEngine() : nullptr),
m_pGearBox(pFacGearBox ? pFacGearBox->CreateGearBox() : nullptr) {};*/
//引入抽象工厂后的写法
CCar(CFactory* pFac):
m_pEngine(pFac ? pFac->CreateEngine() : nullptr),
m_pGearBox(pFac? pFac->CreateGearBox() : nullptr) {};
void drive() {
if (m_pEngine && m_pGearBox) {
m_pEngine->working();
m_pGearBox->working();
cout << "汽车行驶中" << endl;
}
}
~CCar() {
if (m_pEngine)
delete m_pEngine;
m_pEngine = nullptr;
if (m_pGearBox)
delete m_pGearBox;
m_pGearBox = nullptr;
}
};
int main()
{
CFactory* pFac = new CFac2LAuto;
CCar bmw(pFac);
bmw.drive(); //2.0L发动机正在工作 自动挡变速箱正在工作 汽车行驶中
}
对于具体工厂类可以根据需求自由组合产品,如需求增加 CEngine2T和 CGearboxAuto 的组合,则新建工厂类CFactory2TAuto即可。
三种工厂方式总结:
1、对于简单工厂和工厂方法来说,两者的使用方式实际上是一样的,如果对于产品的分类和名称是确定的,数量是相对固定的,推荐使用简单工厂模式。
2、抽象工厂用来解决相对复杂的问题,适用于一系列相关或相互依赖的、大批量的对象生产。
5.中介者模式
- 属于行为型模式,用一个中介对象来封装一系列的对象交互
- 中介者使各个对象不需要显式地互相引用,从而使其耦合松散,并且可以独立地改变它们之间的交互
符合迪米特原则,降低耦合增强复用。
应用场景:INetMediator相当于中介者,负责INet与CKernel之间的交互。
6.观察者模式
- 属于行为型模式
- 定义了一种一对多的依赖关系,让多个观察者对象同时监听某一个主题对象(被观察者)
- 这个主题对象在状态发生变化时,会通知所有观察者对象,使它们能够自动更新自己
#include <iostream>
#include <list>
#include <algorithm>
// 观察者抽象类
class Observer {
public:
virtual void update(int value) = 0;
};
// 具体观察者
class ConcreteObserver : public Observer {
private:
std::string name;
public:
ConcreteObserver(const std::string& n) : name(n) {}
void update(int value) override {
std::cout << "Observer " << name << " received: " << value << std::endl;
}
};
// 主题抽象类
class Subject {
public:
virtual void attach(Observer* observer) = 0;
virtual void detach(Observer* observer) = 0;
virtual void notify() = 0;
};
// 具体主题
class ConcreteSubject : public Subject {
private:
std::list<Observer*> observers;
int state;
public:
void attach(Observer* observer) override {
observers.push_back(observer);
}
void detach(Observer* observer) override {
observers.remove(observer);
}
void notify() override {
for (Observer* observer : observers) {
observer->update(state);
}
}
void setState(int value) {
state = value;
notify();
}
int getState() {
return state;
}
};
int main() {
ConcreteSubject subject;
ConcreteObserver observer1("Observer1");
ConcreteObserver observer2("Observer2");
subject.attach(&observer1);
subject.attach(&observer2);
subject.setState(10); // 触发通知
//输出
//Observer Observer1 received: 10
//Observer Observer2 received: 10
subject.detach(&observer1);
subject.setState(20); // 触发通知
//输出
//Observer Observer2 received: 20
return 0;
}
7. 发布-订阅模式
发布-订阅模式属于行为型模式。
发布 - 订阅模式和观察者模式在概念上非常相似,它们都用于实现对象间的一对多依赖关系,当一个对象的状态发生改变时,所有依赖它的对象都会得到通知并自动更新。但严格来说,二者还是存在一些细微的差别:
观察者模式
- 结构:观察者模式中,被观察者直接维护一个观察者列表,当被观察者的状态发生变化时,它会直接调用观察者列表中每个观察者的更新方法,进行消息的传递。也就是说,被观察者和观察者之间是直接关联的,没有中间层。
发布 - 订阅模式
- 结构:发布 - 订阅模式引入了一个中间层,即消息代理或事件通道。发布者将消息发布到消息代理,订阅者则从消息代理订阅感兴趣的消息。发布者和订阅者之间不直接通信,而是通过消息代理进行间接通信。
解耦程度
- 观察者模式:被观察者和观察者之间仍然存在一定的耦合,因为被观察者需要知道观察者的接口,并且要维护观察者列表。
- 发布 - 订阅模式:发布者和订阅者完全解耦,它们只需要和消息代理进行交互,彼此不需要知道对方的存在,这种解耦程度更高,使得系统的可扩展性和灵活性更强。
Qt的信号槽机制使用的是观察者模式, Redis哨兵集群使用的是发布-订阅模式。
8.策略模式
属于行为型模式,它定义了算法家族,分别封装起来,让它们之间可以互相替换,此模式让算法的变化不会影响到使用算法的用户
比如商场打折,用户有三种选择,分别是满100减10,打七折,满200返30,我们根据用户选择的类型创建对应的方法(可以使用简单工厂),用户不需要知道具体实现的方法。
#include <iostream>
#include <algorithm>
using namespace std;
// Strategy 算法类接口
class Strategy {
public:
virtual ~Strategy() {}
virtual int Count(int amount) = 0;
};
// StrategyA: 满100减10
class StrategyA : public Strategy {
public:
int Count(int amount) override {
int count = amount;
int discount = (amount / 100) * 10;
count -= discount;
return count;
}
};
// StrategyB: 打7折
class StrategyB : public Strategy {
public:
int Count(int amount) override {
return static_cast<int>(amount * 0.7);
}
};
// StrategyC: 满200返30
class StrategyC : public Strategy {
public:
int Count(int amount) override {
int count = amount;
while (count >= 200) {
count -= 30;
}
return count;
}
};
// Context 上下文类
class Context {
private:
Strategy* strategy;
public:
Context() : strategy(nullptr) {}
~Context() {
delete strategy;
strategy = nullptr;
}
void CreateStrategy(int type) {
switch (type) {
case 1:
strategy = new StrategyA();
break;
case 2:
strategy = new StrategyB();
break;
case 3:
strategy = new StrategyC();
break;
default:
delete strategy;
strategy = nullptr;
cout << "未知的策略类型" << endl;
}
}
int GetResult(int amount) {
if (strategy) {
return strategy->Count(amount);
}
cout << "策略未设置" << endl;
return -1;
}
};
int main() {
Context context;
int amount = 300; // 假设消费金额为300元
// 使用策略A
context.CreateStrategy(1);
std::cout << "策略A的结果: " << context.GetResult(amount) << std::endl;
// 使用策略B
context.CreateStrategy(2);
std::cout << "策略B的结果: " << context.GetResult(amount) << std::endl;
// 使用策略C
context.CreateStrategy(3);
std::cout << "策略C的结果: " << context.GetResult(amount) << std::endl;
return 0;
}
优点: 方便扩展新的策略功能的实现
缺点: 使用简单工厂的逻辑,添加新实现,需要改工厂逻辑的代码
9.适配器模式
- 属于结构型模式。
- 目的是解决接口不兼容的问题,使得一个接口可以适配成另一个接口。
- 通过创建一个适配器类来实现接口转换,让不兼容的接口能够合作工作。
- 应用场景:当需要将现有类的接口转化为期望的接口时使用。
假设我们有一个旧的系统使用 OldSystem
类来处理字符串操作,但现在我们需要一个新的接口 NewSystem
来进行操作。我们可以使用适配器模式将旧系统的接口适配到新系统中:
#include <iostream>
#include <string>
// 新系统的接口
class NewSystem {
public:
virtual void processString(const std::string& str) = 0;
};
// 旧系统的接口
class OldSystem {
public:
void oldMethod(const std::string& str) {
std::cout << "OldSystem processing: " << str << std::endl;
}
};
// 适配器类,将 OldSystem 转换为 NewSystem 接口
class Adapter : public NewSystem {
private:
OldSystem& oldSystem; // 持有旧系统的实例
public:
Adapter(OldSystem& oldSystem) : oldSystem(oldSystem) {}
// 适配方法,将新接口方法调用转发到旧接口的方法
void processString(const std::string& str) override {
oldSystem.oldMethod(str);
}
};
int main() {
OldSystem oldSystem; // 创建旧系统对象
Adapter adapter(oldSystem); // 创建适配器
// 使用新系统的接口处理字符串
adapter.processString("Hello, Adapter Pattern!");
return 0;
}
代码解释:
- NewSystem 接口:这是我们期望的目标接口,它定义了
processString
方法。 - OldSystem 类:这是现有的系统接口,它定义了一个方法
oldMethod
。 - Adapter 类:这个类是适配器,它继承自
NewSystem
并持有一个OldSystem
对象。processString
方法会调用OldSystem
中的oldMethod
来完成具体的字符串处理任务。 - 客户端代码:客户端通过适配器
Adapter
来使用旧系统的功能,而不需要直接用OldSystem
。
扩展:应用场景,项目中用到了吗,为什么采用它?
如有错误,欢迎交流指正!