C++ 设计模式是软件工程中应对复杂系统设计的方法,它总结了一系列在软件开发中反复出现的问题及其优雅的解决方案。掌握设计模式能显著提升代码的可读性、可维护性和可扩展性。
一.创建型模式
核心思想非常直观:将对象的创建过程与使用过程解耦(即将对象的创建与使用分离)
1.单例模式
单例模式 (Singleton):保证一个类在整个系统中只有一个实例,并提供一个全局访问点。常用于全局配置管理器、日志记录器、数据库连接池等。
核心要素:
1.将构造函数设置为私有的
2.定义静态的对象,并提供一个共有的静态成员函数
3.将拷贝构造函数与赋值重载函数删除。
(1)饿汉式单例模式
饿汉式单例模式:特点:在程序进入 main 函数之前,静态对象 instance 就已经完成了构造,
优点:线程安全(无需加锁)
缺点:可能造成资源浪费(如果程序没有使用,这里也要进行构造)
cpp
class Singleton
{
public:
static Singleton* getinstance()
{
return &instance;
}
private:
static Singleton instance;
Singleton()
{
}
Singleton(const Singleton&) = delete;
Singleton& operator=(const Singleton&) = delete;
};
Singleton Singleton::instance;
(2)懒汉式单例模式
汉式单例模式的特点是"延迟加载",只有当客户端第一次调用 getInstance() 方法请求实例时,它才会去创建这个唯一的对象。
优点:节省资源
缺点:需要自己实现线程安全(手动加锁)
线程安全的懒汉式单例模式:
锁+双重判断,(锁不易加在if语句外,这会导致单线程情况下,程序也需要加锁,解锁,锁加在if内部需要双重判断,否则依然存在线程安全问题)
cpp
std::mutex mtx;
class Singleton
{
public:
static Singleton* getinstance()
{
if (instance == nullptr)
{
lock_guard<mutex>lock(mtx);
if (instance == nullptr)
{
instance = new Singleton();
}
}
return instance;
}
private:
static Singleton*volatile instance;
Singleton()
{
}
Singleton(const Singleton&) = delete;
Singleton& operator=(const Singleton&) = delete;
};
Singleton* Singleton::instance=nullptr;
(3)Meyers Singleton单例实现方法
Meyers Singleton 被公认为是 C++11 及以后版本中最推荐、最优雅的单例实现方式.,
它结合了"懒汉式"的延迟加载优势(节省启动时间)和"饿汉式"的线程安全及自动管理优势,同时代码量最少,最不容易出错,函数静态局部变量的初始化,在汇编指令上已经自动添加线程互斥指令了。
cpp
class Singleton
{
public:
static Singleton* getinstance()
{
static Singleton instance;
return &instance;
}
private:
Singleton()
{
}
Singleton(const Singleton&) = delete;
Singleton& operator=(const Singleton&) = delete;
};
2.工厂模式
它的核心思想非常直观:将对象的创建和使用分离,简单来说,就是不要在业务代码里到处写 new 来实例化对象,而是把这些复杂的创建逻辑(比如初始化参数、选择具体子类等)统一交给一个专门的"工厂"去负责。调用者只需要告诉工厂"我要什么",工厂就会把造好的对象交给你,你完全不需要关心对象是怎么造出来的。
(1)简单工厂模式
它通过一个专门的工厂类,根据传入的参数(比如字符串或枚举),用 if-else 或 switch-case 来决定创建哪一种产品。
适用场景 :产品种类较少,且基本不会频繁变动。
优缺点:实现简单,调用者不需要关心创建细节。但它严重违反了开闭原则(对扩展开放,对修改关闭)。一旦要新增一种产品,就必须去修改工厂类里的判断逻辑,容易导致代码臃肿。
我们这里为Car类设计一个简单工厂,并提供A1.A2两个种产品类型。
cpp
class Car//汽车基类
{
public:
Car(string name) :_name(name) {}
virtual void show() = 0;
protected:
string _name;
};
class A1 :public Car//汽车类型A1
{
public:
A1(string name) :Car(name) {}
void show() { cout << "A1" <<" " << _name << endl; }
};
class A2 :public Car//汽车类型A2
{
public:
A2(string name) :Car(name) {}
void show() { cout << "A2" <<" " << _name << endl; }
};
enum Cartype
{
A1Type, A2Type
};
class SimpleFactory//简单工厂
{
public:
Car* createCar(Cartype ct)
{
switch (ct)
{
case A1Type:
return new A1("a1");
case A2Type:
return new A2("a2");
default:
cout << "传入参数不正确" << endl;
return nullptr;
}
}
};
(2)工厂方法模式
为了解决简单工厂违反开闭原则的问题,工厂方法模式把"工厂"也抽象化了。它定义了一个创建对象的接口,但由子类(具体工厂)来决定实例化哪一个类。
适用场景 :产品类型会不断扩展,希望新增产品时完全不改动原有代码。
优缺点:完美符合开闭原则。新增产品时,只需要新增一个具体产品类和对应的具体工厂类。缺点是类的数量会成倍增加,系统复杂度变高。
我们可以将上面的简单工厂改成工厂方法:
cpp
class Car//汽车基类
{
public:
Car(string name) :_name(name) {}
virtual void show() = 0;
protected:
string _name;
};
class A1 :public Car//汽车类型A1
{
public:
A1(string name) :Car(name) {}
void show() { cout << "A1" <<" " << _name << endl; }
};
class A2 :public Car//汽车类型A2
{
public:
A2(string name) :Car(name) {}
void show() { cout << "A2" <<" " << _name << endl; }
};
class Factory//工厂基类
{
public:
virtual Car* createCar() = 0;
};
class A1Factory :public Factory//工厂A1
{
public:
Car* createCar()
{
return new A1("a1");
}
};
class A2Factory :public Factory//工厂A2
{
public:
Car* createCar()
{
return new A2("a2");
}
};
(3)抽象工厂模式
当产品不再是单一的一种,而是一个产品族时,就需要抽象工厂。它提供一个接口,用于创建一系列相关或相互依赖的对象。
适用场景 :需要创建一系列相互依赖的对象,并要保证它们之间的兼容性(比如跨平台的 UI 组件库,Windows 风格的一套,Mac 风格的一套)。
优缺点:保证了产品族内部的一致性。但扩展新的产品等级结构(比如给汽车再加一个"音响"配件)非常麻烦,需要修改所有工厂接口(即这里如果在抽象工厂中添加了一个方法,那么其他所有的子工厂都要重写这个方法,哪怕这个方法是它们不需要的)。
我们这里另外设计了一个Flight产品:
cpp
class Car//汽车产品基类
{
public:
Car(string name) :_name(name) {}
virtual void show() = 0;
protected:
string _name;
};
class A1 :public Car//汽车类型A1
{
public:
A1(string name) :Car(name) {}
void show() { cout << "A1" <<" " << _name << endl; }
};
class A2 :public Car//汽车类型A2
{
public:
A2(string name) :Car(name) {}
void show() { cout << "A2" <<" " << _name << endl; }
};
class Flight//飞机基类
{
public:
Flight(string name) :_name(name) {}
virtual void show() = 0;
protected:
string _name;
};
class F1 :public Flight//飞机类型F1
{
public:
F1(string name) :Flight(name) {}
void show() { cout << "F1" << " " << _name << endl; }
};
class F2 :public Flight//飞机类型F2
{
public:
F2(string name) :Flight(name) {}
void show() { cout << "F2" << " " << _name << endl; }
};
class AbstractFactory//抽象工厂基类
{
public:
virtual Car* createCar() = 0;
virtual Flight* createFlight() = 0;
};
class AF1Factory :public AbstractFactory//工厂AF1,负责生产A1,F1
{
public:
Car* createCar()
{
return new A1("a1");
}
Flight* createFlight()
{
return new F1("f1");
}
};
class AF2Factory :public AbstractFactory//工厂AF2,负责生产A2,F2
{
public:
Car* createCar()
{
return new A2("a2");
}
Flight* createFlight()
{
return new F2("f2");
}
};
int main()
{
AbstractFactory* factoryA1 = new AF1Factory();
AbstractFactory* factoryA2 = new AF2Factory();
factoryA1->createCar()->show();
factoryA2->createCar()->show();
factoryA1->createFlight()->show();
factoryA2->createFlight()->show();
}
二.结构型模式
结构型模式主要解决 的是"如何将类或对象组合成更大的结构"。
如果说创建型模式(如工厂)关注的是"怎么 new 对象",那么结构型模式关注的就是"怎么把对象拼起来用"。它们就像建筑里的"连接件",负责把独立的类组装成灵活且稳固的系统。
1.代理模式
核心作用:为对象提供一个替身(代理),以控制对这个对象的访问。代理通常负责对象的创建、销毁或访问前后的预处理。
使用场景:远程代理,虚拟代理,保护代理
优缺点:职责分离,控制访问,但引入了额外的中间层(间接性),可能会带来轻微的性能开销,且增加了类的数量。
我们这里设计一个视频网站观看权限的例子,我们这里对用户不同的身份进行了代理,来负责对象的访问权限问题:
cpp
class VideoSite//视频网站基类
{
public:
virtual void freemovie() = 0;
virtual void VIPmovie() = 0;
};
class VideoSiteBackend :public VideoSite//委托类
{
public:
void freemovie()
{
cout << "see freemovie" << endl;
}
void VIPmovie()
{
cout << "see VIPmovie" << endl;
}
};
class Ordinarymember :public VideoSite//普通用户的代理
{
public:
Ordinarymember() { p = new VideoSiteBackend(); }
~Ordinarymember() { delete p; }
void freemovie()
{
p->freemovie();
}
void VIPmovie()
{
cout << "Insuffcient permissions at present " << endl;
}
private:
VideoSite* p;
};
class VIPmember :public VideoSite//VIP用户的代理
{
public:
VIPmember() { p = new VideoSiteBackend(); }
~VIPmember() { delete p; }
void freemovie()
{
p->freemovie();
}
void VIPmovie()
{
p->VIPmovie();
}
private:
VideoSite* p;
};
int main()
{
unique_ptr< VideoSite>p1(new Ordinarymember());
unique_ptr< VideoSite>p2(new VIPmember());
p1->freemovie();
p1->VIPmovie();
p2->freemovie();
p2->VIPmovie();
}
2.装饰器模式
核心作用 :在不改变原有对象结构的前提下,动态地为对象添加新的功能或职责,是继承的灵活替代方案。
使用场景:要给对象动态增加功能,且这些功能可以随意组合,不想通过大量继承来扩展功能(避免"子类爆炸")
优点 :
比继承更灵活:可以在运行时自由叠加、撤销职责。
符合开闭原则:新增功能只需增加新的装饰器类,无需修改原类。
缺点 :
多层装饰会导致产生很多小对象,增加系统的复杂性,且调试排查问题(堆栈跟踪)会比较麻烦。
例如,我们这里想要给我们的两种汽车添加两种功能,这两种功能对于两种汽车是相同的,那么如果使用继承的话,我们就需要为两种汽车各自创建一个子类,但是我们这里使用装饰器,可以减少子类,降低继承。
cpp
class Car//汽车基类
{
public:
Car(){}
Car(string name) :_name(name) {}
virtual void show() = 0;
protected:
string _name;
};
class A1 :public Car//汽车类型A1
{
public:
A1(string name) :Car(name) {}
void show() { cout << "A1" << " " << _name << endl; }
};
class A2 :public Car//汽车类型A2
{
public:
A2(string name) :Car(name) {}
void show() { cout << "A2" << " " << _name << endl; }
};
class Decorate1:public Car//装饰器1
{
public:
Decorate1(Car* p):_p(p){ }
public:
void show()
{
_p->show();
cout << "功能1" << endl;
}
private:
Car* _p;
};
class Decorate2 :public Car//装饰器2
{
public:
Decorate2(Car* p) :_p(p) {}
public:
void show()
{
_p->show();
cout << "功能2" << endl;
}
private:
Car* _p;
};
int main()
{
Car* p1 = new Decorate1(new A1("a1"));
p1 = new Decorate2(p1);
p1->show();//递归调用
Car* p2 = new Decorate1(new A2("a2"));
p2 = new Decorate2(p2);
p2->show();
}
3.适配器模式
核心作用:解决接口不兼容的问题,充当"转接头",让原本因为接口不匹配而无法一起工作的类能够协同工作。
使用场景:需要复用现有的类,但它的接口与当前系统的需求不兼容,统一多个风格迥异的接口
优点 :
提高兼容性:让原本不相关的类可以一起工作。
遵循开闭原则:通过增加适配器来扩展,无需修改原有代码。
缺点 :
过多使用适配器会让系统变得杂乱,增加代码的复杂度和理解难度。
我们这里实现一个接口转化的适配器:
cpp
class VGA
{
public:
virtual void show()=0;
};
class TV :public VGA//TV需要VGA接口
{
void show()
{
cout << "VGA connection" << endl;
}
};
class HDMI
{
public:
virtual void show() = 0;
};
class Computer
{
public:
void show(HDMI* p)//电脑只支持HDMI接口
{
p->show();
}
};
class HDMItoVGAAdapt:public HDMI//将HDMI接口转化为VGA接口
{public:
ComputerAdapt(TV* p):_p(p){}
void show()
{
_p->show();
}
private:
VGA* _p;
};
int main()
{
Computer computer;
computer.show(new HDMItoVGAAdapt(new TV()));//电脑通过接口投影到TV
}
三.行为型模式
1.观察者模式(发布-订阅模式)
是软件开发中最经典、最常用的一种行为型设计模式。
核心作用:定义对象间的一种一对多的依赖关系,当一个对象(被观察者)的状态发生改变时,所有依赖于它的对象(观察者)都会得到通知并被自动更新。
使用场景:凡是一个动作需要触发多个后续流程,或者一个对象的状态变化需要通知其他多个对象的场景,都可以考虑使用观察者模式(如:图形用户界面(GUI)事件处理,实时数据更新,跨系统的消息交换)
优点:高度解耦(核心优势),易于扩展(符合开闭原则)
我们这里设计一个主题,两个观察者,观察者1对消息1感兴趣,观察者2对消息2感兴趣,当主题的消息1更新,那么感兴趣的所有观察者(这里只有观察者1)都会收到消息,观察者2同理。
cpp
class Observer
{
public:
virtual void handle(int msgid) = 0;
};
class Observer1 :public Observer
{
void handle(int msgid)
{
switch (msgid)
{
case 1:
cout << "Observer1 recv 1 msg " << endl;
break;
}
}
};
class Observer2 :public Observer
{
void handle(int msgid)
{
switch (msgid)
{
case 2:
cout << "Observer2 recv 2 msg " << endl;
break;
}
}
};
class Subject
{
public:
void addObserver(Observer* obser, int msgid)
{
mp[msgid].push_back(obser);
}
void dispatch(int msgid)
{
auto it = mp.find(msgid);
if (it != mp.end())
{
for (Observer* pObser : it->second)
{
pObser->handle(msgid);
}
}
}
private:
unordered_map<int, list<Observer*>> mp;
};
int main()
{
Subject subject;
Observer* p1 = new Observer1();
Observer* p2 = new Observer2();
subject.addObserver(p1, 1);
subject.addObserver(p2, 2);
subject.dispatch(1);
subject.dispatch(2);
}