当前位置: 首页 > news >正文

OOP经典设计模式


课程总目录


文章目录

  • 一、设计模式
  • 二、单例模式(创建型模式)
    • 1.1 饿汉式单例模式
    • 1.2 懒汉式单例模式
    • 1.3 线程安全的懒汉式单例模式
  • 三、工厂模式(创建型模式)
    • 3.1 简单工厂模式
    • 3.2 工厂方法模式
    • 3.3 抽象工厂模式
  • 四、代理模式(结构型模式)
  • 五、装饰器模式(结构型模式)
  • 六、适配器模式(结构型模式)
  • 七、观察者模式(行为型模式)


一、设计模式

设计模式是一种解决特定问题的预定义优秀代码框架,相较于自己摸索解决方案,它具有以下优点:

  1. 易维护:代码更易于维护,具有良好的可读性、复用性、可移植性和健壮性。

  2. 适应需求变化:设计模式的应用遵循 “开闭原则”(对修改关闭,对扩展开放),使得在需求变更或增加新功能时,能够灵活调整。

  3. 模块化设计:合理选择设计模式可以实现高内聚、低耦合的模块化设计,遵循软件设计的基本原则。

设计模式的分类:

  • 创建型模式:关注于对象的创建过程,尝试将对象的创建与使用分离,以增加系统的灵活性和可维护性
  • 结构型模式:关注于如何将类或对象组合成更大的结构,以解决更大的问题
  • 行为型模式:关注于对象之间的交互和职责分配,以实现更好的系统行为和更灵活的协作

23种设计模式:

  • 创建型模式(5种)单例模式(Singleton)工厂方法模式(Factory Method)抽象工厂模式(Abstract Factory)、建造者模式(Builder)、原型模式(Prototype)
  • 结构型模式(7种)适配器模式(Adapter)、桥接模式(Bridge)、装饰器模式(Decorator)、组合模式(Composite)、外观模式(Facade)、享元模式(Flyweight)、代理模式(Proxy)
  • 行为型模式(11种):模板方法模式(Template Method)、命令模式(Command)、迭代器模式(Iterator)、观察者模式(Observer)、中介者模式(Mediator)、备忘录模式(Memento)、访问者模式(Visitor)、状态模式(State)、策略模式(Strategy)、职责链模式(Chain of Responsibility)、解释器模式(Interpreter)

二、单例模式(创建型模式)

单例模式:一个类不管创建多少次对象,永远只能得到该类类型的唯一一个实例对象,那么设计一个单例就必须要满足下面几个条件:

  1. 构造函数私有化:通过将构造函数私有化,防止外部直接创建实例,从而控制实例的创建
  2. 定义该类的唯一实例:在类内部定义一个该类的静态实例,并确保只有一个实例被创建
  3. 通过静态方法返回唯一实例:提供一个静态方法用于访问或获取这个唯一实例,确保每次调用返回相同的实例
  4. 删除拷贝构造和赋值运算符重载:防止通过拷贝构造函数或赋值操作符创建新的实例

单例模式分类:

  1. 饿汉式单例模式

    • 特点:实例在类加载时就被创建,无论是否需要使用实例
    • 优点:天然线程安全,因为实例在类加载过程中创建,不存在多线程访问问题
    • 缺点:即使没有使用实例,资源也会被占用,可能导致资源浪费
  2. 懒汉式单例模式

    • 特点:实例在第一次访问时才被创建,采用延迟加载机制
    • 优点:延迟实例化,只有在需要时才创建实例,节省资源
    • 缺点线程不安全,需要处理多线程环境中的同步问题,以确保实例的唯一性,可能影响性能

单例模式的应用场景举例:

  • 日志模块:整个应用通常只需要一个日志模块实例,以统一管理日志的输出
  • 数据库模块:在客户端和服务器端之间进行通信时,使用单例模式可以确保只有一个数据库连接实例,从而减少资源消耗和连接管理的复杂性
  • 配置管理:应用程序中配置数据通常是全局的,并且需要统一访问,可以使用单例模式来管理
  • 线程池:使用单例模式可以确保只有一个线程池实例,便于统一管理和调度线程
  • 缓存:单例模式可以用于实现应用的全局缓存,以提高访问速度和性能

1.1 饿汉式单例模式

代码示例:

class Singleton
{
public:// 3. 提供一个静态方法返回唯一实例的引用static Singleton* getInstance() { return &instance; }private:static Singleton instance;	// 2. 定义该类的唯一实例对象(使用静态变量,在数据段上)Singleton() { cout << "Singleton()" << endl; }	// 1.构造函数私有化~Singleton() { cout << "~Singleton()" << endl; }// 4. 删除拷贝构造和赋值函数Singleton(const Singleton&) = delete;Singleton& operator=(const Singleton&) = delete;
};Singleton Singleton::instance;	// 类外初始化静态成员变量

测试用例:

int main()
{Singleton* p1 = Singleton::getInstance();Singleton* p2 = Singleton::getInstance();Singleton* p3 = Singleton::getInstance();cout << p1 << endl;cout << p2 << endl;cout << p3 << endl;return 0;
}

测试结果:

Singleton()
00B5E1C8
00B5E1C8
00B5E1C8
~Singleton()

可以看到,该程序通过饿汉式单例模式实现了单例的创建和管理,确保了整个程序中Singleton类只有一个实例。程序输出验证了单例模式的正确性,所有获取的引用都指向同一个实例

1.2 懒汉式单例模式

代码示例:

class Singleton
{
public:// 3. 提供一个静态方法返回唯一实例的引用static Singleton* getInstance(){if (nullptr == instance)instance = new Singleton();return instance;}// 提供一个方法用于销毁实例static void destroyInstance(){if (instance != nullptr){delete instance;instance = nullptr;}}
private:static Singleton* instance;	// 2. 定义该类的唯一实例对象(使用静态变量)Singleton() { cout << "Singleton()" << endl; }	// 1.构造函数私有化~Singleton() { cout << "~Singleton()" << endl; }// 4. 删除拷贝构造和赋值函数Singleton(const Singleton&) = delete;Singleton& operator=(const Singleton&) = delete;
};Singleton* Singleton::instance = nullptr;	// 类外初始化静态成员变量

测试用例:

int main()
{Singleton* p1 = Singleton::getInstance();Singleton* p2 = Singleton::getInstance();Singleton* p3 = Singleton::getInstance();cout << p1 << endl;cout << p2 << endl;cout << p3 << endl;// 在程序结束前销毁实例Singleton::destroyInstance();return 0;
}

测试结果:

Singleton()
013FC3E0
013FC3E0
013FC3E0
~Singleton()

1.3 线程安全的懒汉式单例模式

上面实现的懒汉式单例模式是有问题的,其中getInstance函数不是可重入函数,会有线程安全的问题

getInstance有三个步骤:开辟内存、构造对象、给instance赋值(编译器翻译成汇编指令的顺序或者也可以是开辟内存、给instance赋值、构造对象)

无论两种情况中的哪种,只要是线程一在给instance赋值之前,如果有线程二进入此函数,就会造成线程不安全。

临界区代码段我们要对其保证原子操作,改进一下:

std::mutex mtx;class Singleton
{
public:// 3. 提供一个静态方法返回唯一实例的引用static Singleton* getInstance(){std::lock_guard<std::mutex> lock(mtx);if (nullptr == instance)instance = new Singleton();return instance;}...
};

这里还是有一些不妥,因为我们只考虑了多线程,没有考虑单线程的情况。现在锁的粒度就太大了,单线程的环境下也要频繁的加锁解锁

再次改进一下:

static Singleton* getInstance()
{if (nullptr == instance){// 锁 + 双重判断std::lock_guard<std::mutex> lock(mtx);if (nullptr == instance)instance = new Singleton();}return instance;
}

同时要注意,因为instance是一个指针在数据段,是同一个进程多个线程共享的内存,CPU在执行线程指令的时候为了加快指令的执行,会让线程把共享内存中的值拷贝到自己的线程缓存中

所以要给instance加一个volatile,这是给指针加的(不是给指针的指向加的)。好处是当一个线程对instance赋值时,其他线程马上均能看到instance的改变。因为线程现在已经不对共享变量进行缓存了,大家看的都是其原始内存中的值

class Singleton
{
private:static Singleton* volatile instance;
};ingleton* volatile Singleton::instance = nullptr;	// 类外初始化静态成员变量

再来看一种线程安全的懒汉式单例模式,这次不用互斥锁

代码示例:

class Singleton
{
public:// 3. 提供一个静态方法返回唯一实例的引用static Singleton* getInstance(){static Singleton instance;	// 2. 定义该类的唯一实例对象(使用静态变量)return &instance;}
private:Singleton() { cout << "Singleton()" << endl; }	// 1.构造函数私有化~Singleton() { cout << "~Singleton()" << endl; }// 4. 删除拷贝构造和赋值函数Singleton(const Singleton&) = delete;Singleton& operator=(const Singleton&) = delete;
};

分析:静态对象第一次初始化是在第一次运行到它的时候,也就是当我们没有调用getInstance之前对象都不会构造的。而且通过在Linux中g++ -o main main.cpp -g调试到getInstance函数,观察底层汇编指令,我们可以发现,函数静态局部变量的初始化在汇编指令上已经自动添加线程互斥指令了,也就是已经线程安全了!

测试用例:

int main()
{Singleton* p1 = Singleton::getInstance();Singleton* p2 = Singleton::getInstance();Singleton* p3 = Singleton::getInstance();cout << p1 << endl;cout << p2 << endl;cout << p3 << endl;return 0;
}

测试结果:

Singleton()
00C0E201
00C0E201
00C0E201
~Singleton()

三、工厂模式(创建型模式)

工厂模式是一个比较常用的创建型设计模式,主要是封装了对象的创建,其中可以细分为三种:简单工厂模式(Simple Factory)、工厂方法模式(Factory Method)、抽象工厂模式(Abstract Factory)

  • 简单工厂模式:

    • 定义:简单工厂模式只有一个工厂类,通过该工厂类的静态方法来创建具体的产品对象。工厂类根据不同的参数决定实例化哪一个产品类(简单工厂模式不属于标准OOP设计模式中的一项)
    • 适用场景:适用于产品种类较少且不经常扩展的场合。可以快速创建不同的产品对象,但扩展性有限
  • 工厂方法模式:

    • 定义:工厂方法模式由一个抽象工厂类和多个具体工厂类组成。每个具体工厂类负责创建一个具体产品对象。通过继承抽象工厂类的方法,可以多态地创建不同的具体产品
    • 适用场景:适用于某个产品种类中包含多个具体产品的情况。能够通过增加新的工厂类来扩展产品系列
  • 抽象工厂模式:

    • 定义:抽象工厂模式由一个抽象工厂类和多个具体工厂类组成,同时多个产品接口或抽象类。每个具体工厂类可以创建一组相关的产品对象
    • 适用场景:适用于涉及多个产品种类的复杂系统,可以创建一组相关或依赖的产品对象。适合于需要创建多个系列产品的情况

工厂方法与抽象工厂区别:工厂方法模式只有一个抽象产品类,而抽象工厂模式有多个。 工厂方法模式的具体工厂类只能创建一个具体产品类的实例,而抽象工厂模式可以创建多个。因此,工厂方法模式可以被视为抽象工厂模式的一个特例或简化形式

3.1 简单工厂模式

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 CarType
{BMW, AUDI
};class SimpleFactory
{
public:static Car* createCar(CarType ct){switch (ct){case BMW:return new Bmw("X3");case AUDI:return new Audi("A8");default:cerr << "传入参数不正确" << endl;return nullptr;}}
};int main()
{// Car* p1 = SimpleFactory::createCar(BMW);// Car* p2 = SimpleFactory::createCar(AUDI);// if (p1) p1->show();// if (p2) p2->show();// delete p1;// delete p2;// 改成智能指针,自动释放资源unique_ptr<Car> p1(SimpleFactory::createCar(BMW));unique_ptr<Car> p2(SimpleFactory::createCar(AUDI));if (p1) p1->show();if (p2) p2->show();return 0;
}

现在,简单工厂模式就实现了,但是这种方法有一些缺陷,因为同一个工厂不会又造宝马又造奥迪,又造手机又造键盘的,这一般不符合实际的情况,而且最重要的是这个工厂不符合“开闭原则”,如果我们要增删车辆,就要修改接口,这样很不好。

基于这样的缺陷,接下来看看工厂方法模式

3.2 工厂方法模式

// 产品和上面一样
class Car { ... };
class Bmw : public Car  { ... };
class Audi : public Car  { ... };// 工厂方法
class Factory
{
public:virtual Car* createCar(string name) = 0; // 工厂方法
};
// 宝马工厂
class BMWFactory : public Factory
{
public:Car* createCar(string name) override { return new Bmw(name); }
};
// 奥迪工厂
class AudiFactory : public Factory
{
public:Car* createCar(string name) override { return new Audi(name); }
};int main()
{unique_ptr<Factory> bmwfty(new BMWFactory());unique_ptr<Factory> audifty(new AudiFactory());unique_ptr<Car> p1(bmwfty->createCar("X6"));unique_ptr<Car> p2(audifty->createCar("A8"));p1->show();p2->show();return 0;
}

现在符合“开闭原则”,如果现在想加一个奔驰工厂,直接增加BenzFactory类就行了

3.3 抽象工厂模式

class Car {
public:virtual void show() = 0;
};class Engine {
public:virtual void type() = 0;
};
// ===================================================
class BMWCar : public Car {
public:void show() override { cout << "BMW car" << endl; }
};class BMWEngine : public Engine {
public:void type() override { cout << "BMW engine" << endl; }
};
// ===================================================
class AudiCar : public Car {
public:void show() override { cout << "Audi car" << endl; }
};class AudiEngine : public Engine {
public:void type() override { cout << "Audi engine" << endl; }
};
// ===================================================
class AbstractFactory {
public:	// 产品簇virtual Car* createCar() = 0;virtual Engine* createEngine() = 0;
};class BMWFactory : public AbstractFactory {
public:Car* createCar() override { return new BMWCar(); }Engine* createEngine() override { return new BMWEngine(); }
};class AudiFactory : public AbstractFactory {
public:Car* createCar() override { return new AudiCar(); }Engine* createEngine() override { return new AudiEngine(); }
};
// ===================================================
int main()
{// 创建具体工厂unique_ptr<AbstractFactory> bmwfty(new BMWFactory());unique_ptr<AbstractFactory> audifty(new AudiFactory());// 使用工厂创建产品unique_ptr<Car> bmwCar(bmwfty->createCar());unique_ptr<Engine> bmwEngine(bmwfty->createEngine());unique_ptr<Car> audiCar(audifty->createCar());unique_ptr<Engine> audiEngine(audifty->createEngine());// 调用产品方法if (bmwCar) bmwCar->show();if (bmwEngine) bmwEngine->type();if (audiCar) audiCar->show();if (audiEngine) audiEngine->type();return 0;
}

四、代理模式(结构型模式)

代理模式:通过一个代理对象来控制实际对象的访问权限

例如:客户 ↔ \leftrightarrow 助理(代理) ↔ \leftrightarrow 老板(委托类:实际对象)

代码示例:

// 抽象类:视频站点
class VideoSite
{
public:virtual void freeMovie() = 0;	// 免费电影virtual void vipMovie() = 0;	// VIP电影virtual void ticketMovie() = 0; // 用券观看电影virtual ~VideoSite() = default; // 虚析构函数以确保派生类正确析构
};// 委托类:实际对象(IQIYI 视频站点)
class IQIYIVideoSite : public VideoSite
{
public:virtual void freeMovie() { cout << "观看免费电影" << endl; }virtual void vipMovie() { cout << "观看VIP电影" << endl; }virtual void ticketMovie() { cout << "用券观看电影" << endl; }
};// 代理类:免费用户代理
class FreeProxy : public VideoSite
{
public:FreeProxy() { Video = new IQIYIVideoSite(); }~FreeProxy() { delete Video; }// 通过代理类中重写的方法,来访问真正委托类的方法virtual void freeMovie() { Video->freeMovie(); }virtual void vipMovie() { cout << "您目前只是普通游客,需要升级成VIP,才能观看VIP电影" << endl; }virtual void ticketMovie() { cout << "您目前没有券,需要购买电影券,才能观看电影" << endl; }
private:VideoSite* Video; // 持有实际对象的指针
};// 代理类:VIP用户代理
class VipProxy : public VideoSite
{
public:VipProxy() { Video = new IQIYIVideoSite(); }~VipProxy() { delete Video; }// 通过代理类中重写的方法,来访问真正委托类的方法virtual void freeMovie() { Video->freeMovie(); }virtual void vipMovie() { Video->vipMovie(); }virtual void ticketMovie() { cout << "您目前没有券,需要购买电影券,才能观看电影" << endl; }
private:VideoSite* Video; // 持有实际对象的指针
};// 代理类:使用电影券用户代理
class TicketProxy : public VideoSite 
{
public:TicketProxy() { Video = new IQIYIVideoSite(); }~TicketProxy() { delete Video; }// 通过代理类中重写的方法,来访问真正委托类的方法virtual void freeMovie() { Video->freeMovie(); }virtual void vipMovie() { Video->vipMovie(); }virtual void ticketMovie() { Video->ticketMovie(); }
private:VideoSite* Video; // 持有实际对象的指针
};// 这些都是通用的API接口,使用的都是基类的指针或者引用
void watchMovie(unique_ptr<VideoSite>& ptr) 
{ptr->freeMovie();ptr->vipMovie();ptr->ticketMovie();
}int main() {unique_ptr<VideoSite> p1(new FreeProxy);   // 创建免费用户代理对象unique_ptr<VideoSite> p2(new VipProxy);    // 创建VIP用户代理对象unique_ptr<VideoSite> p3(new TicketProxy); // 创建使用电影券用户代理对象watchMovie(p1); // 观看电影,使用免费用户代理cout << "===================" << endl;watchMovie(p2); // 观看电影,使用VIP用户代理cout << "===================" << endl;watchMovie(p3); // 观看电影,使用电影券用户代理return 0;
}

运行结果:

观看免费电影
您目前只是普通游客,需要升级成VIP,才能观看VIP电影
您目前没有券,需要购买电影券,才能观看电影
===================
观看免费电影
观看VIP电影
您目前没有券,需要购买电影券,才能观看电影
===================
观看免费电影
观看VIP电影
用券观看电影

五、装饰器模式(结构型模式)

装饰器模式:可以动态地添加行为到对象中,而不需要修改类的定义

目的: 为了增强现有类的功能

关键参与者:

  • Component(组件):定义一个对象接口,可以给这些对象动态地添加职责
  • ConcreteComponent(具体组件):实现了组件接口的类
  • Decorator(装饰器):持有一个组件对象的引用或指针,定义一个与组件接口一致的接口,并且将所有请求委托给该组件对象
  • ConcreteDecorator(具体装饰器):扩展装饰器功能的类,实现额外的行为

代码示例(普通指针版本):

// 抽象基类(组件)
class Car
{
public:virtual void show() = 0;virtual ~Car() = default; // 虚析构函数
};// 三种汽车(具体组件)
class Bmw : public Car
{
public:void show() override { cout << "这是一辆宝马汽车,配置有:基本配置"; }
};class Audi : public Car
{
public:void show() override { cout << "这是一辆奥迪汽车,配置有:基本配置"; }
};class Benz : public Car
{
public:void show() override { cout << "这是一辆奔驰汽车,配置有:基本配置"; }
};// 装饰器基类
class CarDecorator : public Car
{
public:CarDecorator(Car* car) : pCar(car) {}virtual ~CarDecorator() { delete pCar; } // 确保释放被装饰的对象
protected:Car* pCar;
};// 装饰器1:定速巡航
class CruiseControl : public CarDecorator
{
public:CruiseControl(Car* car) : CarDecorator(car) {}void show() override{pCar->show();cout << ", 定速巡航";}
};// 装饰器2:自动刹车
class AutoBrake : public CarDecorator
{
public:AutoBrake(Car* car) : CarDecorator(car) {}void show() override{pCar->show();cout << ", 自动刹车";}
};// 装饰器3:车道偏离
class LaneDepartureWarning : public CarDecorator
{
public:LaneDepartureWarning(Car* car) : CarDecorator(car) {}void show() override{pCar->show();cout << ", 车道偏离";}
};int main()
{Car* bmw = new Bmw();Car* p1 = new CruiseControl(bmw);p1 = new AutoBrake(p1);p1 = new LaneDepartureWarning(p1);p1->show();cout << endl;delete p1; // 确保释放内存Car* audi = new Audi();Car* p2 = new AutoBrake(audi);p2->show();cout << endl;delete p2; // 确保释放内存Car* benz = new Benz();Car* p3 = new LaneDepartureWarning(benz);p3->show();cout << endl;delete p3; // 确保释放内存return 0;
}

代码示例(智能指针版本):

// 抽象基类(组件)
class Car
{
public:virtual void show() = 0;virtual ~Car() = default; // 添加虚析构函数
};// 三种汽车(具体组件)
class Bmw : public Car
{
public:void show() override { cout << "这是一辆宝马汽车,配置有:基本配置"; }
};class Audi : public Car
{
public:void show() override { cout << "这是一辆奥迪汽车,配置有:基本配置"; }
};class Benz : public Car
{
public:void show() override { cout << "这是一辆奔驰汽车,配置有:基本配置"; }
};// 装饰器基类
class CarDecorator : public Car
{
public:CarDecorator(shared_ptr<Car> car) : pCar(car) {}virtual void show() = 0; // 纯虚函数,必须由子类实现
protected:shared_ptr<Car> pCar;
};// 装饰器1:定速巡航
class CruiseControl : public CarDecorator
{
public:CruiseControl(shared_ptr<Car> car) : CarDecorator(car) {}void show() override{pCar->show();cout << ", 定速巡航";}
};// 装饰器2:自动刹车
class AutoBrake : public CarDecorator
{
public:AutoBrake(shared_ptr<Car> car) : CarDecorator(car) {}void show() override{pCar->show();cout << ", 自动刹车";}
};// 装饰器3:车道偏离
class LaneDepartureWarning : public CarDecorator
{
public:LaneDepartureWarning(shared_ptr<Car> car) : CarDecorator(car) {}void show() override{pCar->show();cout << ", 车道偏离";}
};int main()
{shared_ptr<Car> p1 = make_shared<CruiseControl>(make_shared<Bmw>());p1 = make_shared<AutoBrake>(p1);p1 = make_shared<LaneDepartureWarning>(p1);p1->show();cout << endl;shared_ptr<Car> p2 = make_shared<AutoBrake>(make_shared<Audi>());p2->show();cout << endl;shared_ptr<Car> p3 = make_shared<LaneDepartureWarning>(make_shared<Benz>());p3->show();cout << endl;return 0;
}

运行结果:

这是一辆宝马汽车,配置有:基本配置, 定速巡航, 自动刹车, 车道偏离
这是一辆奥迪汽车,配置有:基本配置, 自动刹车
这是一辆奔驰汽车,配置有:基本配置, 车道偏离

六、适配器模式(结构型模式)

适配器模式:使不兼容的接口能够一起工作

电源转换插头、HDMI转VGA的线,这些都是适配器模式在生活中的例子

代码示例:

// VGA 接口类
class VGA
{
public:virtual void play() = 0;virtual ~VGA() = default;
};// 具体的 VGA 投影仪
class VGAProjector : public VGA
{
public:void play() override { cout << "通过 VGA 接口连接投影仪,进行视频播放" << endl; }
};// HDMI 接口类
class HDMI
{
public:virtual void play() = 0;virtual ~HDMI() = default;
};// 具体的 HDMI 投影仪
class HDMIProjector : public HDMI
{
public:void play() override { cout << "通过 HDMI 接口连接投影仪,进行视频播放" << endl; }
};// 适配器类,将 VGA 接口转换为 HDMI 接口
class VGAToHDMIAdapter : public VGA
{
public:VGAToHDMIAdapter(HDMI* p) : pHdmi(p) {}void play() override { pHdmi->play(); }
private:HDMI* pHdmi;
};// 电脑类,只支持 VGA 接口
class Computer
{
public:void playVideo(VGA* pVGA) { pVGA->play(); }
};int main() {// 创建 Computer 对象Computer* computer = new Computer();// 通过 VGA 接口连接投影仪VGAProjector* vgaProjector = new VGAProjector();computer->playVideo(vgaProjector);// 通过 HDMI 接口连接投影仪,使用适配器HDMIProjector* hdmiProjector = new HDMIProjector();VGAToHDMIAdapter* adapter = new VGAToHDMIAdapter(hdmiProjector);computer->playVideo(adapter);// 手动释放所有动态分配的内存delete adapter;delete hdmiProjector;delete vgaProjector;delete computer;return 0;
}

运行结果:

通过 VGA 接口连接投影仪,进行视频播放
通过 HDMI 接口连接投影仪,进行视频播放

七、观察者模式(行为型模式)

观察者模式,又名发布-订阅模式事件监听器模式,主要关注对象之间的一对多依赖关系。当一个对象(称为主题或被观察者)的状态发生改变时,所有依赖于它的对象(称为观察者)都会收到通知并自动更新

代码示例:

// 观察者抽象类
class Observer
{
public:// 处理消息的接口virtual void handle(int msgid) = 0;virtual ~Observer() = default;
};// 第一个观察者实例:对消息1和消息2感兴趣
class Observer1 : public Observer
{
public:void handle(int msgid) override{switch (msgid){case 1:cout << "Observer1 received message 1" << endl;break;case 2:cout << "Observer1 received message 2" << endl;break;default:cout << "Observer1 received unknown message" << endl;break;}}
};// 第二个观察者实例:对消息2感兴趣
class Observer2 : public Observer
{
public:void handle(int msgid) override{if (msgid == 2){cout << "Observer2 received message 2" << endl;}else {cout << "Observer2 received unknown message" << endl;}}
};// 第三个观察者实例:对消息1和消息3感兴趣
class Observer3 : public Observer
{
public:void handle(int msgid) override{switch (msgid){case 1:cout << "Observer3 received message 1" << endl;break;case 3:cout << "Observer3 received message 3" << endl;break;default:cout << "Observer3 received unknown message" << endl;break;}}
};// 主题类
class Subject {
public:// 添加观察者void addObserver(Observer* observer, int msgid){observers[msgid].push_back(observer);}// 通知观察者void notifyObservers(int msgid){auto it = observers.find(msgid);if (it != observers.end())for (Observer* observer : it->second)observer->handle(msgid);}private:unordered_map<int, list<Observer*>> observers; // 观察者列表,按消息ID分类
};int main() {Subject subject;Observer* p1 = new Observer1();Observer* p2 = new Observer2();Observer* p3 = new Observer3();// 注册观察者到不同的消息IDsubject.addObserver(p1, 1);subject.addObserver(p1, 2);subject.addObserver(p2, 2);subject.addObserver(p3, 1);subject.addObserver(p3, 3);int msgid = 0;while (true){cout << "输入消息ID (-1 退出): ";cin >> msgid;if (msgid == -1)break;subject.notifyObservers(msgid);}// 清理内存delete p1;delete p2;delete p3;return 0;
}

运行结果:

输入消息ID (-1 退出): 1
Observer1 received message 1
Observer3 received message 1
输入消息ID (-1 退出): 2
Observer1 received message 2
Observer2 received message 2
输入消息ID (-1 退出): 3
Observer3 received message 3
输入消息ID (-1 退出): -1

相关文章:

  • Docker 入门全攻略:安装、操作与常用命令指南
  • Java 实现括号匹配:栈的应用与优化
  • zabbix的主/动模式自定义监控项
  • LCM红外小目标检测
  • 【人工智能】Transformers之Pipeline(八):文生图/图生图(text-to-image/image-to-image)
  • C语言之“ 分支和循环 ” (2)
  • 阿里云CDN-边缘脚本EdgeScript的CI/CD实践
  • MTK Android12 SystemUI 手势导航 隐藏导航栏底部布局
  • Tomcat 使用和配置文件(详解)
  • Spring Boot - 通过ServletRequestHandledEvent事件实现接口请求的性能监控
  • <数据集>停车场空位识别数据集<目标检测>
  • LabVIEW位移检测系统
  • 【CPP】slt-list由认识到简化模拟实现深度理解~
  • 储能集装箱动环监控系统,动环监控在集装箱的应用方案@卓振思众
  • 安科瑞Home EMS:引领家庭光储新纪元,让每一度电都尽在掌握
  • 10个确保微服务与容器安全的最佳实践
  • eclipse的离线汉化
  • express如何解决request entity too large问题
  • GDB 调试 Mysql 实战(三)优先队列排序算法中的行记录长度统计是怎么来的(上)...
  • JAVA之继承和多态
  • laravel 用artisan创建自己的模板
  • Promise面试题2实现异步串行执行
  • React+TypeScript入门
  • spring boot 整合mybatis 无法输出sql的问题
  • supervisor 永不挂掉的进程 安装以及使用
  • Three.js 再探 - 写一个跳一跳极简版游戏
  • 聚类分析——Kmeans
  • 漫谈开发设计中的一些“原则”及“设计哲学”
  • 浅析微信支付:申请退款、退款回调接口、查询退款
  • 世界编程语言排行榜2008年06月(ActionScript 挺进20强)
  • 无服务器化是企业 IT 架构的未来吗?
  • ‌U盘闪一下就没了?‌如何有效恢复数据
  • #if和#ifdef区别
  • #NOIP 2014# day.2 T2 寻找道路
  • #vue3 实现前端下载excel文件模板功能
  • ( 10 )MySQL中的外键
  • (20)docke容器
  • (2020)Java后端开发----(面试题和笔试题)
  • (done) NLP “bag-of-words“ 方法 (带有二元分类和多元分类两个例子)词袋模型、BoW
  • (floyd+补集) poj 3275
  • (Git) gitignore基础使用
  • (三维重建学习)已有位姿放入colmap和3D Gaussian Splatting训练
  • (十一)手动添加用户和文件的特殊权限
  • (转)c++ std::pair 与 std::make
  • (转)大道至简,职场上做人做事做管理
  • .NET C# 配置 Options
  • .NET Micro Framework初体验(二)
  • .NET 给NuGet包添加Readme
  • .NET 事件模型教程(二)
  • .NET/C# 编译期间能确定的相同字符串,在运行期间是相同的实例
  • .NET/C# 编译期能确定的字符串会在字符串暂存池中不会被 GC 垃圾回收掉
  • [ 隧道技术 ] 反弹shell的集中常见方式(二)bash反弹shell
  • []常用AT命令解释()
  • [Android实例] 保持屏幕长亮的两种方法 [转]
  • [C#]winform部署PaddleOCRV3推理模型