C++ 设计模式
工厂模式:
我们需要方便的使用这些类,减少耦合度
#include <iostream>
#include <string>
using namespace std;
class car{
public:
car(string name):name_(name){}
virtual void show();
protected:
string name_;
};
class aodi:public car
{
public:
aodi(string name):car(name){}
void show(){
cout<<"this is aodi car!"<<name_<<endl;
}
};
class baoma:public car{
public:
baoma(string name):car(name){}
void show(){
cout<<"this is baoma car!"<<name_<<endl;
}
};
简单工厂
简单工厂模式的优缺点
优点
- 代码简洁: 只需一个工厂类即可创建所有产品对象,代码结构清晰易懂。
- 降低耦合: 客户端无需了解具体产品的创建细节,只需通过工厂类即可获取所需产品对象,降低了客户端与产品的耦合度。
- 方便扩展: 只需修改工厂类即可添加新产品,无需修改客户端代码,提高了系统的扩展性。
缺点
- 工厂类职责过重: 工厂类集中了所有产品的创建逻辑,一旦工厂类出现问题,将导致所有产品无法创建。
- 违背开闭原则: 添加新产品需要修改工厂类,违背了开闭原则。
- 灵活性差: 工厂类创建产品对象的逻辑是固定的,无法根据需求动态调整。
适用场景
- 产品种类较少,且需求相对稳定,无需频繁添加新产品。
- 产品之间具有共同的父类或接口,可以抽象出创建产品的通用逻辑。
- 需要降低客户端与产品的耦合度,提高代码的简洁性。
我们用代码例子来理解
enum car_type{
AODI,BAOMA
};
//简单工厂
//创建一个枚举,根据枚举来返回类
class Factor{
public:
car * create_car(car_type ctype) const
{
if(ctype == AODI)
{
return new aodi("aodi");
}
else if(ctype == BAOMA)
{
return new baoma("baoma");
}
else return nullptr;
}
};
int main()
{
Factor f;
car * myCar = f.create_car(AODI);
myCar->show();
}
工厂方法
工厂方法的优缺点
工厂方法模式是一种创建型设计模式,它定义了一个创建对象的接口,让子类决定创建哪种对象的具体实现。
优点:
- 封装性: 工厂方法模式将对象的创建逻辑封装在工厂类中,客户端无需了解具体对象的创建细节,提高了代码的封装性。
- 灵活性: 工厂方法模式可以根据需求动态创建不同的对象,提高了代码的灵活性。
- 扩展性: 工厂方法模式可以很容易地扩展新的产品类型,符合开闭原则。
缺点:
- 代码量增加: 工厂方法模式需要为每个产品类型创建一个工厂类,会增加代码量。
- 抽象层次增加: 工厂方法模式引入了一个抽象的工厂类,增加了代码的复杂度。
适用场景:
- 产品种类较多,且需求相对稳定,无需频繁添加新产品。
- 产品之间具有共同的父类或接口,可以抽象出创建产品的通用逻辑。
- 需要降低客户端与产品的耦合度,提高代码的灵活性。
与简单工厂模式的比较:
- 简单工厂模式将所有产品的创建逻辑集中在一个工厂类中,代码结构简单,但灵活性差,违背开闭原则。
- 工厂方法模式将创建产品的逻辑分散到多个工厂类中,提高了灵活性,符合开闭原则,但也增加了代码量。
class Factor{
public:
virtual car* createCar(string)=0;
};
class aodiFactor:public Factor{
public:
car* createCar(string name){
return new aodi(name);
}
};
class baomaFactor:public Factor{
public:
car* createCar(string name){
return new baoma(name);
}
};
抽象工厂
抽象工厂的优缺点
抽象工厂模式是一种创建型设计模式,它提供了一个创建相关产品对象的接口,让子类决定创建哪种具体产品族的对象。
优点:
- 封装性: 抽象工厂模式将创建产品的逻辑封装在工厂类中,客户端无需了解具体产品的创建细节,提高了代码的封装性。
- 灵活性: 抽象工厂模式可以根据需求动态创建不同产品族的对象,提高了代码的灵活性。
- 扩展性: 抽象工厂模式可以很容易地扩展新的产品族,符合开闭原则。
- 强制一致性: 抽象工厂模式可以确保客户端只能使用同一个产品族中的对象,提高了代码的一致性。
缺点:
- 代码量增加: 抽象工厂模式需要为每个产品族创建一个工厂类,会增加代码量。
- 抽象层次增加: 抽象工厂模式引入了一个抽象的工厂类和一系列抽象产品类,增加了代码的复杂度。
适用场景:
- 需要创建一系列相关的对象,并且这些对象之间存在一定的约束关系的场景。
- 产品种类较多,且需求相对稳定,无需频繁添加新产品。
- 需要降低客户端与产品的耦合度,提高代码的灵活性。
与工厂方法模式的比较:
- 工厂方法模式只针对单个产品类型,而抽象工厂模式可以针对多个产品类型。
- 抽象工厂模式可以强制一致性,确保客户端只能使用同一个产品族中的对象。
#include <iostream>
#include <string>
#include <memory>
using namespace std;
// 汽车类
class car{
public:
car(string name):name_(name){}
virtual void show()=0;
protected:
string name_;
};
// 车灯类
class light{
public:
virtual void show()=0;
};
// 奥迪汽车
class aodi:public car
{
public:
aodi(string name):car(name){}
void show() override {
cout<<"this is aodi car!"<<name_<<endl;
}
};
// 奥迪车灯
class aodilight:public light{
public:
void show() override{
cout<<"ao di show() light"<<endl;
}
};
// 宝马汽车
class baoma:public car{
public:
baoma(string name):car(name){}
void show() override {
cout<<"this is baoma car!"<<name_<<endl;
}
};
// 宝马车灯
class baomalight:public light{
void show() override{
cout<<"bao ma show() light"<<endl;
}
};
// 抽象工厂模式
class Factor{
public:
virtual car* createCar(string)=0;
virtual light* createLight()=0;
};
// 奥迪工厂
class aodiFactor:public Factor{
public:
car* createCar(string name)override{
return new aodi(name);
}
light* createLight()override{
return new aodilight();
}
};
// 宝马工厂
class baomaFactor:public Factor{
public:
car* createCar(string name)override{
return new baoma(name);
}
light* createLight()override{
return new baomalight();
}
};
int main()
{
unique_ptr<Factor> af(new aodiFactor());
unique_ptr<car> ac(af->createCar("aodi"));
unique_ptr<light> al(af->createLight());
ac->show();
al->show();
}
单例模式
懒汉单例模式:
- 在第一次使用单例对象时才进行实例化
- 优点:节省内存资源,避免在程序启动时就创建不使用的对象
- 缺点:线程安全性问题,需要加锁保证线程安全
饿汉单例模式:
- 在程序启动时就进行实例化
- 优点:线程安全,无需加锁
- 缺点:浪费内存资源,无论是否使用都会在程序启动时创建对象
总结:
特性 | 懒汉单例模式 | 饿汉单例模式 |
---|---|---|
实例化时间点 | 首次使用时 | 程序启动时 |
线程安全性 | 需要加锁 | 无需加锁 |
内存资源 | 节省 | 浪费 |
饿汉单例模式
// 饿汉单例模式
#ifndef 0
class Cl{
public:
static Cl* getCl(){
return &cl_;
}
private:
Cl(const Cl&)=delete;
Cl& operator=(const Cl&)=delete;
Cl(){}
static Cl cl_;
};
Cl Cl::cl_;
#endif
线程安全单例模式
懒汉单例模式
// 懒汉单例模式
mutex mutex1;
class Cl{
public:
static Cl* getCl(){
if(c_ ==nullptr)
{
lock_guard<mutex> lg(mutex1);
c_ = new Cl();
}
return c_;
}
private:
Cl()=default;
Cl(const Cl&)=delete;
Cl& operator=(const Cl&)=delete;
static Cl* c_;
};
Cl* Cl::c_=nullptr;
观察者模式
观察者模式是一种设计模式,它定义了一种一对多的依赖关系,让多个观察者对象同时监听某一个主题对象(被观察者)。当主题对象的状态发生更改时,会通知所有观察者,让它们能够自动更新。
观察者模式的结构:
- 主题对象(Subject): 也叫抽象目标类,它提供了一个用于保存观察者对象的聚集类和增加、删除观察者对象的方法,以及通知所有观察者的抽象方法。
- 具体主题对象(Concrete Subject): 也叫具体目标类,它实现抽象目标中的通知方法,当具体主题的内部状态发生改变时,通知所有注册过的观察者对象。
- 观察者对象(Observer): 也叫抽象观察者类,它定义了观察者需要实现的更新方法。
- 具体观察者对象(Concrete Observer): 具体观察者类,实现抽象观察者类中的更新方法,并根据主题对象的改变做出相应的反应。
观察者模式的优点:
- 降低耦合度:主题对象和观察者对象之间通过接口进行交互,耦合度低,易于维护和扩展。
- 提高灵活性:观察者可以自由注册和注销,主题对象无需关心观察者的具体实现。
- 支持多播:主题对象可以同时通知多个观察者,提高效率。
观察者模式的应用场景:
- 界面更新:当数据发生变化时,自动更新界面。
- 事件处理:当事件发生时,通知所有相关的观察者进行处理。
- 状态监控:监控主题对象的内部状态,并根据状态变化进行相应的处理。
#include <iostream>
#include <unordered_map>
class Observer;
// 抽象主题类
class Subject {
public:
virtual ~Subject() {}
virtual void Attach(Observer* observer) = 0; // 添加观察者
virtual void Detach(Observer* observer) = 0; // 删除观察者
virtual void Notify() = 0; // 通知所有观察者
};
// 抽象观察者类
class Observer {
public:
virtual ~Observer() {}
virtual void Update() = 0; // 更新
};
// 具体主题类
class ConcreteSubject : public Subject {
public:
ConcreteSubject() {}
~ConcreteSubject() {}
void Attach(Observer* observer) override {
observers_[observer] = true; // 将观察者添加到哈希表中
}
void Detach(Observer* observer) override {
observers_.erase(observer); // 从哈希表中删除观察者
}
void Notify() override {
for (auto it = observers_.begin(); it != observers_.end(); ++it) {
it->first->Update(); // 通知所有观察者
}
}
private:
std::unordered_map<Observer*, bool> observers_; // 存储所有观察者的哈希表
};
// 具体观察者类
class ConcreteObserver1 : public Observer {
public:
ConcreteObserver1() {}
~ConcreteObserver1() {}
void Update() override {
std::cout << "ConcreteObserver1 received notification." << std::endl;
}
};
// 具体观察者类
class ConcreteObserver2 : public Observer {
public:
ConcreteObserver2() {}
~ConcreteObserver2() {}
void Update() override {
std::cout << "ConcreteObserver2 received notification." << std::endl;
}
};
int main() {
// 创建主题对象
ConcreteSubject* subject = new ConcreteSubject();
// 创建观察者对象
ConcreteObserver1* observer1 = new ConcreteObserver1();
ConcreteObserver2* observer2 = new ConcreteObserver2();
// 将观察者添加到主题中
subject->Attach(observer1);
subject->Attach(observer2);
// 主题状态发生改变
subject->Notify();
// 删除观察者
subject->Detach(observer1);
// 主题再次发生改变
subject->Notify();
delete subject;
delete observer1;
delete observer2;
return 0;
}
代理模式
为什么需要代理模式
1、客户端可能无法直接操作对象(在另一台机器),那么可以在客户端建立一个代理对象,然后客户端直接调用目标对象,代理对象在与目标对象建立联系
2、需要加强目标对象的功能。有人表示为什么不在当初的类中加好,要使用代理来补充。单一职责要求一个类的功能尽可能单一,而且增强的功能大多都是与之前业务不同,比如增加日志输出,权限判断等,本来就不应该由之前的类去承担
#include <iostream>
class Subject {
public:
virtual ~Subject() {}
virtual void Request() = 0; // 请求方法
};
class RealSubject : public Subject {
public:
RealSubject() {}
~RealSubject() {}
void Request() override {
std::cout << "RealSubject::Request()" << std::endl;
}
};
class Proxy : public Subject {
public:
Proxy() {}
~Proxy() {}
void Request() override {
std::cout << "Proxy::Request()" << std::endl;
realSubject_->Request(); // 调用真实对象的 Request() 方法
}
private:
RealSubject* realSubject_ = new RealSubject(); // 真实对象
};
int main() {
Subject* subject = new Proxy(); // 创建代理对象
subject->Request(); // 调用代理对象的 Request() 方法
delete subject;
return 0;
}
适配器模式
适配器模式介绍
适配器模式是一种结构型设计模式,用于将一个类的接口转换成客户端所期望的另一种接口。适配器模式允许不兼容的接口之间进行协作。它可以将现有代码与外部库、框架或API集成起来,而无需修改已有代码。
适配器模式的结构如下:
- 目标接口:定义客户端期望的接口。
- 适配器类:实现目标接口,并包含对适配者类的引用。
- 适配者类:定义已有的接口。
适配器模式的工作原理如下:
- 客户端想要使用目标接口。
- 适配器类实现了目标接口。
- 适配器类包含对适配者类的引用。
- 适配器类将客户端的请求转发给适配者类。
- 适配者类处理请求并返回结果。
- 适配器类将结果返回给客户端。
为什么要使用适配器模式?
适配器模式具有以下优点:
- 提高代码的复用性:适配器模式允许你重复使用现有的类或接口,而不需要修改它们的代码。
- 提高系统的灵活性:适配器模式可以将不兼容的接口转换为兼容的接口,从而提高了系统的灵活性,使得系统更加容易进行扩展和维护。
- 降低耦合度:适配器模式可以降低类之间的耦合度,使得代码更加易于理解和维护。
适配器模式的应用场景
适配器模式可以应用于以下场景:
- 将旧的接口转换为新的接口:当系统的接口发生变化时,可以使用适配器模式将旧的接口转换为新的接口,从而使旧的代码能够继续使用。
- 将不同的接口统一起来:当系统中存在多个不同的接口时,可以使用适配器模式将这些接口统一起来,从而使客户端能够以统一的方式使用这些接口。
- 将第三方库或框架集成到系统中:当需要将第三方库或框架集成到系统中时,可以使用适配器模式将第三方库或框架的接口转换为系统期望的接口。
适配器模式的示例
以下是一个使用适配器模式的示例:
#include <iostream>
class Target {
public:
virtual void Request() = 0;
};
class Adaptee {
public:
void SpecificRequest() {
std::cout << "SpecificRequest() called." << std::endl;
}
};
class Adapter : public Target {
public:
Adapter(Adaptee* adaptee) : adaptee_(adaptee) {}
void Request() override {
adaptee_->SpecificRequest();
}
private:
Adaptee* adaptee_;
};
int main() {
Adaptee* adaptee = new Adaptee();
Adapter* adapter = new Adapter(adaptee);
adapter->Request();
return 0;
}
在这个示例中,Target
类定义了一个目标接口。Adaptee
类定义了一个已有的接口。Adapter
类实现了目标接口,并包含对 Adaptee
类的引用。
在 main()
函数中,创建了一个 Adaptee
对象和一个 Adapter
对象。Adapter
对象将 Adaptee
对象的 SpecificRequest()
方法包装成 Request()
方法,从而使客户端能够以统一的方式使用 Adaptee
对象。
输出:
SpecificRequest() called.
装饰器模式
装饰器模式是一种结构型设计模式,它允许向一个现有对象添加新的功能,同时又不改变其结构。这种类型的设计模式属于结构型模式,它是作为现有的类的一个包装。这种模式创建了一个装饰类,用来包装原有的类,并在保持类方法签名完整性的前提下,提供了额外的功能。
C++ 中的装饰器模式示例
以下是一个 C++ 中的装饰器模式示例:
#include <iostream>
class Shape {
public:
virtual void draw() = 0;
};
class Circle : public Shape {
public:
void draw() override {
std::cout << "Drawing a circle" << std::endl;
}
};
class Rectangle : public Shape {
public:
void draw() override {
std::cout << "Drawing a rectangle" << std::endl;
}
};
class ColoredShape : public Shape {
protected:
Shape* shape;
std::string color;
public:
ColoredShape(Shape* shape, const std::string& color) : shape(shape), color(color) {}
void draw() override {
shape->draw();
std::cout << " with color " << color << std::endl;
}
};
int main() {
Shape* circle = new Circle();
circle->draw();
Shape* redCircle = new ColoredShape(circle, "red");
redCircle->draw();
Shape* rectangle = new Rectangle();
rectangle->draw();
Shape* greenRectangle = new ColoredShape(rectangle, "green");
greenRectangle->draw();
return 0;
}
在这个示例中,Shape
是一个抽象类,它定义了一个 draw()
方法。Circle
和 Rectangle
是 Shape
的子类,它们分别实现了 draw()
方法来绘制圆形和矩形。
ColoredShape
是一个装饰器类,它包装了一个 Shape
对象并添加了颜色功能。ColoredShape
的 draw()
方法首先调用其包装的 Shape
对象的 draw()
方法,然后输出颜色信息。
在 main 函数中,我们创建了几个形状对象并使用它们来演示装饰器模式。
输出:
Drawing a circle
Drawing a circle with color red
Drawing a rectangle
Drawing a rectangle with color green
在这个示例中,ColoredShape
装饰器类允许我们向 Shape
对象添加颜色功能,而无需修改 Shape
类本身。这使得我们可以灵活地添加新功能到现有对象,而无需改变它们的结构。