观察者模式 C++

👂 Honey Honey - 孙燕姿 - 单曲 - 网易云音乐

目录

🌼前言

🌼描述

🎂问题

💪解决方案

🈲现实场景 + 代码

场景1 -- 报纸发行

场景

解释

代码

场景2 -- 气象资料发布

场景3 -- 过红绿灯

🏔观察者 -- 模式结构

🏦观察者 -- 适用情况

🐟实现方式

😖优缺点

🔚与其他模式的联系


🌼前言

《Linux多线程服务器端编程 使用muduo C++网络库》第 3 页提到,“本书默认大家已熟知 --观察者模式”,特地来学习

🌼描述

  • 观察者模式是一种行为设计模式,允许你定义一种订阅机制,可在对象事件发生时,通知多个 “观察” 该对象的其他对象

  • 定义对象间的一种一对多(变化)的依赖关系,以便当一个对象(Subject)的状态发生改变时,所有依赖于它的对象都得到通知并自动更新
  • 一种行为型的设计模式,QT 中的信号槽就是一种典型的观察者模式

🎂问题

比如你有两种类型的对象:顾客 和 商店。

顾客对某个特定品牌的产品非常感兴趣(比如最新型号的 iphone 手机),该产品很快会在商店里出售。

顾客可以每天到商店里看看 iphone 到货了没。如果最新款 iphone 没到货,大多数到达商店的顾客就会空手而归(浪费资源)

还有个问题是:为了避免 “顾客每天到商店查看到货情况”,商店可以在每次新产品到货时,向 所有顾客 发送邮件,那么部分客户就不用反复前往商店。

但是这会打扰对新产品不感兴趣的顾客

如何解决呢?

💪解决方案

观察者模式 就此登场:

发布者(publisher):自身状态的改变,通知到其他对象。

订阅者(subscribers):希望关注发布者状态变化的对象。

观察者模式中,发布者类拥有自己的  添加订阅机制,每个对象都能订阅或取消订阅发布者事件流

该机制包括:

1)一个用于存储订阅者对象的列表    2)几个用于添加或删除该列表中订阅者的方法

发布者发布通知后,都需要遍历订阅者列表,然后调用每个订阅者对象特定的通知方法

为了解耦订阅者和发布者,所有订阅者都要实现相同接口,发布者只通过接口和订阅者交互

如果有多种不同类型的发布者,而且要求订阅者要兼容所有发布者,那么就让所有发布者拥有相同的接口。

这个接口只需描述几个订阅方法

如此一来,订阅者就可以在不与具体发布者耦合的情况下,通过接口观察发布者的状态

🈲现实场景 + 代码

每个代码都是相似的,不同场景,增加熟练度 

场景1 -- 报纸发行

场景

发布者 -- 出版社

订阅者 -- 渣渣辉

比方说,你订阅了一份报纸,那么就不需要再去报摊查询新出版的报纸了。

出版社(发布者)会在报纸出版后,直接将最新一期报纸寄到你的邮箱。

出版社(发布者)负责维护订阅者列表( subscriber() ),了解订阅者对哪些报纸感兴趣。

当订阅者不感兴趣后,他们随时可以从该列表退出。

解释

解释1 -- 代码中的接口

  1. ISubscriber 和 IPublisher 是两个接口,定义了订阅者和出版者的基本行为。这两个接口是基类。

  2. ISubscriber 接口定义了一个 Update 方法,这是具体订阅者需要实现的方法,用于接收来自出版者的消息。

  3. IPublisher 接口定义了 AttachDetach 和 Notify 方法,分别用于添加订阅者、移除订阅者和通知所有订阅者。

  4. Publisher 是 IPublisher 的子类,实现了 IPublisher 接口的所有方法。它维护了一个订阅者列表,并在有新消息时通知所有订阅者。

  5. Subscriber 是 ISubscriber 的子类,实现了 ISubscriber 接口的 Update 方法。它保存了一个出版者的引用,并可以从出版者的订阅者列表中添加或移除自己。

  6. 在 ClientCode 函数中,创建了一个 Publisher 对象和多个 Subscriber 对象,并通过 Attach 和 Detach 方法管理订阅者列表。当 Publisher 有新消息时,所有在订阅者列表中的 Subscriber 都会收到通知。

总的来说,ISubscriber 和 IPublisher 是基类,定义了一组行为Subscriber 和 Publisher 是子类,实现了这些行为。这是典型的面向对象设计,通过接口和实现分离,使得代码更加灵活和可扩展

解释2 -- 整体逻辑

这段代码实现了观察者模式,

其中包含两个接口:ISubscriber(订阅者)和IPublisher(发布者)

具体的订阅者类Subscriber和发布者类Publisher分别实现了这两个接口

ISubscriber接口定义了一个Update方法,接收发布者的消息

IPublisher接口定义了Attach、Detach和Notify方法(添加一个订阅者、删除一个订阅者,通知所有订阅者)

Publisher类中的订阅者列表 list_subscriber_,存储所有订阅者

有新消息时,Publisher 通过 Notify 方法通知所有订阅者

Publisher 的 CreateMessage 方法,创建新消息 + 通知所有订阅者

Subscriber 类在构造函数中将自己添加到 Publisher 的订阅者列表,Update方法接收来自Publisher的消息

Subscriber 的 RemoveMeFromTheList(),从Publisher的订阅者列表中删除自己

main函数中,创建了一个Publisher对象和三个Subscriber对象

Publisher发布了几条消息,Subscriber接收并打印出这些消息

解释3 -- Publisher 接口

1)为什么 Subscriber 类需要通过通过统一接口 ISubscriber 实现呢?

是为了使 Publisher类 和 Subscriber类 解耦,具体的说👇

IPublisher接口 只依赖于 ISubscriber 接口,而不是具体的 Subscriber类,所以我们可以在不修改 Publisher类 的情况下,添加新的 Subsriber 类型,只要这个新的 Subscriber 类型实现了 ISubscriber 接口即可(这就是“解耦”) 

2)那么为什么 Publisher 类也需要接口 IPublisher 呢?

比如说,你以后想要创建一个新的 Publisher 类,只需要实现 IPublisher 接口即可

任何依赖 IPublisher 接口的代码,都可以无缝衔接地使用这个新的 Publisher 类 

解释4 -- 类的关系

参考博客👇

UML类图六种关系总结 - 知乎 (zhihu.com)

uml 类图依赖与关联的区别 - 掸尘 - 博客园 (cnblogs.com)

  • ​​​​​Publisher 和 ISubscriber 之间是聚合关系
    这就像一个报社(Publisher)和它的订阅者(ISubscriber)之间的关系
    报社是整体,订阅者是部分
    报社可以有很多订阅者,但是即使没有订阅者,报社依然可以存在
    这就是所谓的"has-a"关系。
  • Subscriber 实现了 ISubscriber 接口,Publisher 实现了 IPublisher 接口
    这就像一个具体的订阅者(Subscriber)是订阅者接口(ISubscriber)的具体实现,一个具体的报社(Publisher)是报社接口(IPublisher)的具体实现
    这是一种"is-a"关系

  • main函数(也就是客户端代码)关联了 Publisher,依赖了 Subscriber
    这就像客户端代码知道报社(Publisher)的存在,并且使用了订阅者(Subscriber)
    这是一种"knows-a"关系和"uses-a"关系
    客户端代码知道报社的存在,可以通过报社发布消息
    同时,客户端代码也使用了订阅者,可以创建订阅者,让订阅者订阅报社的消息

代码

#include <iostream> // 输入输出流库
#include <list> 
#include <string>// 订阅者接口
class ISubscriber {
public:virtual ~ISubscriber(){}; // 虚析构函数// 具体订阅者的更新方法;= 0 纯虚函数,子类必须自己实现virtual void Update(const std::string &message_from_publisher) = 0;
};// 出版社接口
class IPublisher {
public:// 纯虚函数:基类不能被实例化,只能在子类实现// 虚析构 -- 保证子类析构时能够调用基类析构virtual ~IPublisher(){};// 添加订阅者;= 0 子类自己实现virtual void Attach(ISubscriber *subscriber) = 0;// 移除订阅者;= 0 子类自己实现virtual void Detach(ISubscriber *subscriber) = 0;// 通知所有订阅者;= 0 子类自己实现virtual void Notify() = 0;
};// 具体出版社类
class Publisher : public IPublisher {
public:virtual ~Publisher() { // 析构std::cout << std::endl;std::cout << "报社要倒闭了!!!" << std::endl;}// override 表示子类覆盖父类的方法 -- 保证方法名和参数一致// 添加 -- 子类实现void Attach(ISubscriber *subscriber) override {list_subscriber_.push_back(subscriber);}// 删除void Detach(ISubscriber *subscriber) override {list_subscriber_.remove(subscriber);}// 通知void Notify() override {std::cout << std::endl;// 订阅者列表迭代器std::list<ISubscriber *>::iterator iterator = list_subscriber_.begin();HowManySubscriber(); // 输出订阅者数量while (iterator != list_subscriber_.end()) {// 更新所有订阅者(*iterator)->Update(message_);++iterator;}}// 创建消息 && 通知订阅者void CreateMessage(std::string message = "Empty") {// Empty 默认值,会被输入值覆盖this->message_ = message;Notify();}// 输出订阅者数量void HowManySubscriber() {std::cout << "有 " << list_subscriber_.size() << " 个傻逼看我的报纸" << std::endl;}// 其他业务逻辑,大事件发生时通知订阅者void SomeBusinessLogic() {this->message_ = "大事件!大事件!"; // 更改消息Notify(); // 通知std::cout << "房价跌到 300 块一平,快点入手!" << std::endl;}private:std::list<ISubscriber *> list_subscriber_; // 订阅者列表std::string message_; // 发送给订阅者的消息
};// 具体的订阅者类
class Subscriber : public ISubscriber {
public:// 构造函数Subscriber(Publisher &publisher) : publisher_(publisher) {this->publisher_.Attach(this); // 添加订阅者std::cout << std::endl;std::cout << "O(∩_∩)O渣渣 "<< ++Subscriber::static_number_ << " 号订阅了报纸" << std::endl;this->number_ = Subscriber::static_number_; // 订阅者编号}virtual ~Subscriber() {std::cout << std::endl;std::cout << "垃圾报纸,毁我青春,注销 " << this->number_ << " 号" << std::endl;}// 用新消息更新订阅者void Update(const std::string &message_from_publisher) override {message_from_publisher_ = message_from_publisher;PrintInfo();}// 从报社的订阅者列表,删除此订阅者void RemoveMeFromTheList() {publisher_.Detach(this);std::cout << std::endl;std::cout << "订阅者 " << number_ << " 不看报纸了" << std::endl;}// 打印新消息void PrintInfo() {std::cout << "订阅者 " << this->number_ << " 号: " << message_from_publisher_ << std::endl;}private:std::string message_from_publisher_; // 来自报社的信息Publisher &publisher_; // 报社的引用static int static_number_; // 订阅者实例的数量int number_; // 订阅者编号/*static 静态成员变量,只有一份拷贝所有实例共享通过类名::静态成员变量名访问类外初始化*/
};// static成员变量,类外初始化
int Subscriber::static_number_ = 0;// 客户端代码
int main() {Publisher *publisher = new Publisher; // 创建报社// 创建 3 个看报纸的人Subscriber *subscriber1 = new Subscriber(*publisher);Subscriber *subscriber2 = new Subscriber(*publisher);Subscriber *subscriber3 = new Subscriber(*publisher);// 报社发布消息 为空publisher->CreateMessage();// 报社发布新消息publisher->CreateMessage("房价又涨了!");// 第 2 个人退出subscriber2->RemoveMeFromTheList(); // 第 4 个人加入Subscriber *subscriber4 = new Subscriber(*publisher);// 发布新消息publisher->CreateMessage("我买了新车!!");// 发布新消息publisher->SomeBusinessLogic(); delete subscriber1;delete subscriber2;delete subscriber3;delete subscriber4;delete publisher; // 报社倒闭return 0;
}

O(∩_∩)O渣渣 1 号订阅了报纸O(∩_∩)O渣渣 2 号订阅了报纸O(∩_∩)O渣渣 3 号订阅了报纸有 3 个傻逼看我的报纸
订阅者 1 号: Empty
订阅者 2 号: Empty
订阅者 3 号: Empty有 3 个傻逼看我的报纸
订阅者 1 号: 房价又涨了!
订阅者 2 号: 房价又涨了!
订阅者 3 号: 房价又涨了!订阅者 2 不看报纸了O(∩_∩)O渣渣 4 号订阅了报纸有 3 个傻逼看我的报纸
订阅者 1 号: 我买了新车!!
订阅者 3 号: 我买了新车!!
订阅者 4 号: 我买了新车!!有 3 个傻逼看我的报纸
订阅者 1 号: 大事件!大事件!
订阅者 3 号: 大事件!大事件!
订阅者 4 号: 大事件!大事件!
房价跌到 300 块一平,快点入手!垃圾报纸,毁我青春,注销 1 号垃圾报纸,毁我青春,注销 2 号垃圾报纸,毁我青春,注销 3 号垃圾报纸,毁我青春,注销 4 号报社要倒闭了!!!

场景2 -- 气象资料发布

IDisplayA 和 IDisplayB 类是订阅者,它们实现了 IDisplay 接口并订阅了 DataCenter 发布的通知。DataCenter 类是发布者,它维护了一个订阅者列表,并在数据变化时通知所有的订阅者

气象站发布气象资料给数据中心,数据中心经过处理,将气象信息更新到两个不同的显示终端(A 和B)

上层:气象站

下层:显示中断

依赖:数据中心

代码2

#include <iostream>
#include <vector>
using namespace std;// IDisplay是一个抽象基类,定义了观察者的接口
class IDisplay {
public:virtual void show(float temperature) = 0;  // 所有的观察者都需要实现这个方法virtual ~IDisplay() = default;  // 虚析构函数,用于删除派生类的对象
};// IDisplayA是一个观察者,实现了IDisplay的接口
class IDisplayA : public IDisplay {
public:void show(float temperature) override {  // 打印温度信息cout << "Display A: " << temperature << endl;}
};// IDisplayB也是一个观察者,实现了IDisplay的接口
class IDisplayB : public IDisplay {
public:void show(float temperature) override {  // 打印温度信息cout << "Display B: " << temperature << endl;}
};// WeatherData是一个数据类,用于存储天气数据
class WeatherData {// 这里可以添加一些属性和方法,例如温度、湿度等
};// DataCenter是主题,它维护了一个观察者列表,并在状态改变时通知所有的观察者
class DataCenter {
public:void Attach(IDisplay *ob) {  // 添加一个观察者obs.push_back(ob);}void Detach(IDisplay *ob) {  // 删除一个观察者obs.erase(remove(obs.begin(), obs.end(), ob), obs.end());}void Notify() {  // 通知所有的观察者float temper = CalcTemperature();for (auto iter = obs.begin(); iter != obs.end(); iter++) {(*iter)->show(temper);}}private:WeatherData* GetWeatherData() {  // 获取天气数据// 这里可以添加一些代码,例如从数据库或者API获取天气数据return new WeatherData;}float CalcTemperature() {  // 计算温度WeatherData *data = GetWeatherData();float temper = 25.0f;  // 这里只是一个示例,实际的温度应该从WeatherData中获取delete data;return temper;}vector<IDisplay*> obs;  // 观察者列表
};int main() {DataCenter *center = new DataCenter;  // 创建一个DataCenter对象IDisplay *da = new IDisplayA;  // 创建一个IDisplayA对象IDisplay *db = new IDisplayB;  // 创建一个IDisplayB对象center->Attach(da);  // 将IDisplayA添加到观察者列表center->Attach(db);  // 将IDisplayB添加到观察者列表center->Notify();  // 通知所有的观察者delete da;  // 删除IDisplayA对象delete db;  // 删除IDisplayB对象delete center;  // 删除DataCenter对象return 0;
}

场景3 -- 过红绿灯

发布者 -- 红绿灯

订阅者 --  汽车

十字路口汽车等待红绿灯变化

代码3

#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;// Observer class, which is an abstract class 
// that other classes can inherit from to become observers
// 订阅者接口 Observer
class Observer {
public:Observer(int num):m_number(num){}// Virtual destructor to ensure correct deletion // when deleting derived classes through a base class pointervirtual ~Observer() {}// Pure virtual function to be implemented by derived classesvirtual void update(bool flag) = 0;
protected:int m_number;
};// Subject class, which is an abstract class 
// that other classes can inherit from to become subjects
// 发布者接口 Subject
class Subject {
public:virtual ~Subject() {}virtual void attach(Observer* observer) = 0;virtual void detach(Observer* observer) = 0;virtual void notify(bool flag) = 0;
protected:vector<Observer*> observers;
};// TrafficLight class, which is a concrete subject 
// that observers can attach to
// 发布者具体实现
class TrafficLight : public Subject {
public:void attach(Observer* observer) override {observers.push_back(observer);}void detach(Observer* observer) override {observers.erase(remove(observers.begin(), observers.end(), observer), observers.end());}// Notify all attached observers with the current statevoid notify(bool flag) override {if (flag) {cout << "Green light, go." << endl;} else {cout << "Red light, stop." << endl;}for (auto observer : observers) {observer->update(flag);}}
};// Car class, which is a concrete observer that can attach to a subject
// 订阅者具体实现
class Car : public Observer {
public:Car(int num) : Observer(num) {}// Update the state of the car based on the state of the traffic lightvoid update(bool flag) override {if (flag) {cout << "Car " << m_number << ": Start." << endl;} else {cout << "Car " << m_number << ": Stop." << endl;}}
};int main() {Subject *subject = new TrafficLight;Observer *car1 = new Car(1);Observer *car2 = new Car(2);// Attach the cars to the traffic lightsubject->attach(car1);subject->attach(car2);// Notify the cars with the state of the traffic lightsubject->notify(true);subject->notify(false);/*一个类有虚函数,那么它应该有一个虚析构函数这样,当删除一个指向派生类对象的基类指针时,派生类的析构函数也会被调用,防止资源泄露*/delete car1;delete car2;delete subject;return 0;
}
Green light, go.
Car 1: Start.
Car 2: Start.
Red light, stop.
Car 1: Stop.
Car 2: Stop.

🏔观察者 -- 模式结构

结合场景1代码,看👇的模式结构图

解释 -- 类图 

- subscriber: Subscriber[]
// 私有 成员变量名: 类型
// subscriber 是类中的一个私有成员变量
// 这个变量的类型是:存储多个 Subscriber 对象的数组
+ subscribe(s: Subscriber)
// 公有 函数名(参数名: 参数类型)

  1. 发布者(Publisher)向其他对象发送信息。Notify() 会在报社自身状态改变 或 执行特定函数后发生。Publisher 类中包含 Detach() 和 Attach(),允许订阅者离开或加入。
  2. 新事件发生时,报社会遍历订阅列表 list_subscriber_,并调用每个订阅者的 Notify(),该方法在 ISubscriber(订阅者接口)声明。
  3. 订阅者接口(ISubscriber):大多数情况,该接口只包含一个 Update() 方法,该方法拥有多个参数来传递信息。
  4. 具体订阅者(Subscriber):执行一些操作回应发布者的通知。所有具体订阅者都实现了同样的接口,因此发布者不需要与具体的类耦合。
  5. 订阅者需要一些上下文信息来正确的更新。所以,发布者要将一些上下文数据,作为 Notify() 的参数传递给订阅者。发布者也可将自身作为参数传递,以便订阅者直接获取数据。
  6. 客户端(Client):分别创建发布者和订阅者,然后为订阅者注册发布者的更新。

🏦观察者 -- 适用情况

  • 当一个对象状态的改变,需要改变其他对象;或实际对象是事先未知的或动态变化时,可以用观察者模式
  • 比如说,使用图形界面时,用户创建了自定义按钮类,并允许客户端在按钮中注入自定义代码,这样当用户按下按钮,就会触发这些代码

观察者模式允许任何实现了订阅者接口的对象,订阅发布者对象的事件通知

你可以在按钮中添加订阅机制,允许客户端通过自定义订阅类注入自定义代码

当应用中的一些对象必须观察其他对象时,可使用观察者模式

但只能在有限时间或特定情况下使用

订阅列表是动态的,因此订阅者可以随时加入或离开该列表

🐟实现方式

  1. 据业务逻辑,将 观察者模式 拆分为两部分:
    a. 独立于其他代码的核心功能(发布者)
    b. 其他代码(一组订阅者类)
  2. 声明订阅者接口:该接口至少应声明一个 update() 方法
  3. 声明发布者接口:并定义一些接口用于在列表中添加 / 删除订阅对象(注意!发布者必须且只能通过订阅者接口和订阅对象进行交互)
  4. 定存放实际订阅列表的位置,并实现订阅方法:
    a. 因为所有类型的发布者都一样,那么,列表应该放置在直接扩展自发布者接口(IPublisher)的具体发布者(Publisher)中。具体发布者会扩展 接口类,从而继承所有的订阅行为
    b. 但是,如果你需要在现有的类层次结构中应用观察者模式,最好使用组合的方式:将订阅逻辑放入一个独立的对象,然后让所有实际订阅者使用该对象
  5. 创建具体发布者类(Publisher):每次发生重要事件,发布者都要通过所有订阅者
  6. 体订阅者类(Subscriber)中实现通知更新的方法:
    a. 订阅者需要一些与事件相关的上下文数据,这些数据(message_from_publisher)作为通知方法的参数来传递
    b. 另一种选择:订阅者从通知中获取 所有数据,此时发布者通过更新方法,将自身传递出去
    c. 还有一种不太灵活的方法:通过构造函数,将发布者与订阅者永久联系起来
  7. 客户端生成所需的全部订阅者,并到相应发布者完成注册工作

😖优缺点

优点

1,开闭原则

你不用修改任何发布者代码就能引用新的订阅者类(良好的扩展性)

(如果是发布者接口,就能轻松引入发布者类)

2,可以在运行时建立对象之间的联系(稳定的信息更新传递机制)

3,耦合双方依赖于抽象,不需要了解具体

缺点

 1,订阅者(也称观察者)的通知顺序是随机的(所以在设计观察者模式时,不要依赖于订阅者的通知顺序,这回让代码变得脆弱)

a. 订阅者运行时动态注册,那么注册顺序可能会变

b. 部分订阅者的注销,会影响剩下订阅者的通知顺序

c. 发布者可以以任何顺序通知定于这

2,某个订阅者出现卡顿,可能会影响整个进程,一般采用异步机制处理,同时注意线程安全

3,订阅者过多时,挨个通知每个订阅者,耗时较长

🔚与其他模式的联系

责任链模式命令模式中介者模式观察者模式 ---- 是用于处理请求发送者和接收者间的不同连接方式

  • 责任链:按照顺序将请求动态传递给一系列的潜在接收者,直至其中一名接收者对请求进行处理
  • 命令:在发送者和接收者之间建立单向连接
  • 中介者:清除发送者和接收者之间的直接连接,强制它们通过一个中介对象进行间接沟通
  • 观察者:允许接收者动态的订阅或取消接收要求

中介者 和 观察者的区别往往很难记住,具体的:

  1. 中介者的主要目标是,消除一系列系统组间之间的相互依赖。这些组间依赖于同一个中介者对象
  2. 观察者的目标是,在对象间建立动态的单向连接,使得部分对象也可作为其他对象的附属发挥作用
  3. 有一种流行的中介者模式的实现,依赖于观察者模式:
    a. 中介者对象担当发布者的角色,其他组件作为订阅者,可以订阅中介者的事件或取消订阅
    b. 当中介者用这种方式实现时,它看起来和观察者一样
  4. 你可能会困惑,此时可以采用其他方式来实现中介者。比如,永久的将所有组件链接到同一个中介者对象。这种实现方式和观察者长的很像,但它仍然是中介者模式
  5. 假设有一个程序,它所有的组件都变成了发布者,它们之间可以相互建立动态连接。这样程序就没有了中心化的中介者对象,只有一些分布式的观察者

参考文章👇

C++ 观察者模式讲解和代码示例 (refactoringguru.cn)

InterviewGuide大厂面试真题

设计模式之观察者模式(C++)-阿里云开发者社区 (aliyun.com)

观察者模式 (datawhalechina.github.io)

在 IDE 中使用 GitHub Copilot Chat - GitHub 文档

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/304054.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Partition分区案例实操

文章目录 问题引出和分析需求代码实现运行结果分区总结 问题引出和分析 要求将统计结果按照条件输出到不同的文件中&#xff08;分区&#xff09;&#xff0c;比如将统计结果按照手机归属地不同省份输出到不同的文件中。 默认的分区&#xff1a; 默认的分区是根据key的hashCo…

文件处理的神器,一键上传签署,安全又高效!

客户介绍 某技术股份有限公司是一家在高科技领域深耕多年的知名企业&#xff0c;专注于技术创新与产品研发&#xff0c;致力于为客户提供前沿、高效的解决方案。在业务范围方面&#xff0c;该公司涵盖了多个领域&#xff0c;包括智能制造、新能源、信息技术等。公司凭借卓越的…

ITK 重采样 resample

Itk 重新采样有二多种情况&#xff0c;这里说二种情况 1. 输入参数 &#xff0c;和输出相关数据&#xff0c;输出范围&#xff0c;spacing &#xff1b; typedef itk::Image< float, 3 > itkFloatImageType;typedef itk::ResampleImageFilter < itkFloatImageType, i…

docker 创建容器过程

结合下图&#xff0c;本文讨论docker 创建容器过程&#xff1a; START└── [用户通过Docker Client发出指令]└── (1) docker run 或 docker create 命令├── (2) Docker Client与Docker Daemon建立通信连接└── (3) Docker Daemon接收到创建容器请求├── (4) 检查…

【MySQL数据库 | 第二十五篇】深入探讨MVCC底层原理

前言&#xff1a; 在当今互联网时代&#xff0c;数据库扮演着数据存储和管理的关键角色。对于大型Web应用程序和企业级系统而言&#xff0c;高效地处理并发访问和事务管理是至关重要的。多版本并发控制&#xff08;MVCC&#xff09;是一种数据库事务处理的技术&#xff0c;旨…

【复现】用友NC-Cloud文件上传漏洞_70

目录 一.概述 二 .漏洞影响 三.漏洞复现 1. 漏洞一&#xff1a; 四.修复建议&#xff1a; 五. 搜索语法&#xff1a; 六.免责声明 一.概述 用友NC Cloud大型企业数字化平台&#xff0c;深度应用新一代数字智能技术&#xff0c;完全基于云原生架构&#xff0c;打造开放、…

【Axure高保真原型】马赛克效果

今天和大家分享动态画出马赛克效果的原型模板&#xff0c;鼠标按着左键拖动&#xff0c;可以在对应位置拖出马赛克&#xff0c;具体效果可以观看下方视频或者打开预览地址体验。 【原型效果】 【Axure高保真原型】马赛克效果 【原型预览含下载地址】 https://cspfgk.axshare.…

一例简单的文件夹病毒的分析

概述 这是一个典型的文件夹病毒&#xff0c;使用xp时代的文件夹图标&#xff0c;通过可移动存储介质传播&#xff0c;会向http://fionades.com/ABIUS/setup.exe下载恶意载荷执行。 其病毒母体只是一个加载器&#xff0c;会在内存是解密加载一个反射型的dll&#xff0c;主要的…

<网络> 网络Socket编程基于TCP协议模拟简易网络通信

目录​​​​​​​ 前言&#xff1a; 一、字符串回响 &#xff08;一&#xff09;程序结构 &#xff08;二&#xff09;初始化服务器 &#xff08;三&#xff09;启动服务器 1. 处理连接请求 2. 业务处理 3. 回调函数 &#xff08;四&#xff09;填充server源文件 &…

春秋云境28512

题目说该CMS的/single.php路径下&#xff0c;id参数存在一个SQL注入漏洞。访问看一下随便点一个图片。 发现了注入点?id 那么开始查看闭合符一个 就报错了 You have an error in your SQL syntax; check the manual that corresponds to your MariaDB server version for th…

Unity多线程简单示例

using UnityEngine; using System.Threading;public class texxxst : MonoBehaviour {Thread thread;void Start(){// 创建一个新的线程&#xff0c;并传入要执行的方法thread new Thread(new ThreadStart(DoWork));// 启动线程thread.Start();}void DoWork(){for (int i 0; …

【机器学习】机器学习创建算法第5篇:线性回归,学习目标【附代码文档】

机器学习&#xff08;算法篇&#xff09;完整教程&#xff08;附代码资料&#xff09;主要内容讲述&#xff1a;机器学习算法课程定位、目标&#xff0c;K-近邻算法定位,目标,学习目标,1 什么是K-近邻算法,1 Scikit-learn工具介绍,2 K-近邻算法API。K-近邻算法&#xff0c;1.4 …

Day30 回溯 LeedCode 332.重新安排行程 51. N皇后 37. 解数独 蓝桥杯 与或异或

332. 重新安排行程 给你一份航线列表 tickets &#xff0c;其中 tickets[i] [fromi, toi] 表示飞机出发和降落的机场地点。请你对该行程进行重新规划排序。 所有这些机票都属于一个从 JFK&#xff08;肯尼迪国际机场&#xff09;出发的先生&#xff0c;所以该行程必须从 JFK…

2、Qt UI控件 -- qucsdk项目使用

前言&#xff1a;上一篇文章讲了qucsdk的环境部署&#xff0c;可以在QDesigner和Qt Creator中看到qucsdk控件&#xff0c;这一篇来讲下在项目中使用qucsdk库中的控件。 一、准备材料 要想使用第三方库&#xff0c;需要三个先决条件&#xff0c; 1、控件的头文件 2、动/静态链…

LeetCode 239. 滑动窗口最大值

滑动窗口最大值 给你一个整数数组 nums&#xff0c;有一个大小为 k 的滑动窗口从数组的最左侧移动到数组的最右侧。你只可以看到在滑动窗口内的 k 个数字。滑动窗口每次只向右移动一位。 返回 滑动窗口中的最大值 。 示例 1&#xff1a; 输入&#xff1a;nums [1,3,-1,-3,…

数据生成 | Matlab实现基于DE差分进化算法的数据生成

数据生成 | Matlab实现基于DE差分进化算法的数据生成 目录 数据生成 | Matlab实现基于DE差分进化算法的数据生成生成效果基本描述模型描述程序设计参考资料 生成效果 基本描述 1.Matlab实现基于DE差分进化算法的数据生成&#xff0c;运行环境Matlab2021b及以上&#xff1b; 2.计…

FreeRTOS任务切换学习

FreeRTOS任务切换学习 所谓任务切换&#xff0c;就是CPU寄存器的切换。假设当由任务A切换到任务B时&#xff0c;主要分为两步&#xff1a; 1&#xff1a;需暂停任务A的执行&#xff0c;并将此时任务A的寄存器保存到任务堆栈&#xff0c;这个过程叫做保存现场&#xff1b; 2&am…

设计模式-接口隔离原则

基本介绍 客户端不应该依赖它不需要的接口&#xff0c;即一个类对另一个类的依赖应该建立在最小的接口上先看一张图: 类A通过接口Interface1 依赖类B&#xff0c;类C通过接口Interface1 依赖类D&#xff0c;如果接口Interface1对于类A和类C来说不是最小接口&#xff0c;那么类…

Unity之PUN实现多人联机射击游戏的优化(Section 2)

目录 &#x1f3ae;一、准备工作 &#x1f3ae;二、实现手雷投掷动作 &#x1f3ae;三、手雷投掷同步 &#x1f4a4;3.1 photonView.RPC &#x1f3ae;四、同步手雷伤害 这几周都给我布置任务了&#xff0c;最近可忙。现在终于有机会更新了&#xff0c;也谢谢大家的阅读&a…

爬虫 新闻网站 以湖南法治报为例(含详细注释) V1.0

目标网站&#xff1a;湖南法治报 爬取目的&#xff1a;为了获取某一地区更全面的在湖南法治报已发布的宣传新闻稿&#xff0c;同时也让自己的工作更便捷 环境&#xff1a;Pycharm2021&#xff0c;Python3.10&#xff0c; 安装的包&#xff1a;requests&#xff0c;csv&#xff…