观察者模式-改进

代码还可以从以下几个方面进行改进,提升代码的健壮性、灵活性和性能:

1. 使用 std::weak_ptr 避免循环引用

在观察者模式中,如果 SubjectObserver 之间的关系过于复杂,且双方都使用 std::shared_ptr 彼此引用,可能会导致循环引用(也称为“循环依赖”),使得智能指针无法释放内存,导致内存泄漏。

改进措施:使用 std::weak_ptr 打破循环引用。std::weak_ptr 不增加引用计数,它只持有对象的弱引用。

在这个例子中,Subject 持有观察者的 std::weak_ptr,而观察者依旧可以使用 std::shared_ptr

#include <iostream>
#include <vector>
#include <string>
#include <memory>

class Observer {
public:
    virtual ~Observer() {}
    virtual void update(const std::string& message_from_subject) = 0;
};

class Subject {
public:
    virtual ~Subject() {}
    virtual void attach(std::shared_ptr<Observer> observer) = 0;
    virtual void detach(std::shared_ptr<Observer> observer) = 0;
    virtual void notify() = 0;
};

class ConcreteSubject : public Subject {
private:
    std::vector<std::weak_ptr<Observer>> observers;  // 使用weak_ptr打破循环引用
    std::string message;
public:
    void attach(std::shared_ptr<Observer> observer) override {
        observers.push_back(observer);
    }

    void detach(std::shared_ptr<Observer> observer) override {
        observers.erase(
            std::remove_if(observers.begin(), observers.end(), 
                           [&observer](const std::weak_ptr<Observer>& o) {
                               return o.lock() == observer;
                           }), 
            observers.end());
    }

    void notify() override {
        for (auto it = observers.begin(); it != observers.end();) {
            if (auto observer = it->lock()) {  // lock() 将 weak_ptr 转换为 shared_ptr
                observer->update(message);
                ++it;
            } else {
                it = observers.erase(it);  // 如果对象已被销毁,移除观察者
            }
        }
    }

    void createMessage(const std::string& message) {
        this->message = message;
        notify();
    }
};

class ConcreteObserver : public Observer {
private:
    std::string name;
    std::string message_from_subject;
public:
    ConcreteObserver(const std::string& name) : name(name) {}

    void update(const std::string& message_from_subject) override {
        this->message_from_subject = message_from_subject;
        display();
    }

    void display() const {
        std::cout << "Observer [" << name << "] received message: " << message_from_subject << std::endl;
    }
};

int main() {
    auto subject = std::make_shared<ConcreteSubject>();

    auto observer1 = std::make_shared<ConcreteObserver>("Observer 1");
    auto observer2 = std::make_shared<ConcreteObserver>("Observer 2");
    auto observer3 = std::make_shared<ConcreteObserver>("Observer 3");

    subject->attach(observer1);
    subject->attach(observer2);
    subject->attach(observer3);

    subject->createMessage("Hello Observers!");

    subject->detach(observer2);  // 移除Observer 2

    subject->createMessage("New Update Available!");

    return 0;
}

改进要点

  • std::weak_ptr 用于 Subject 持有的观察者列表,避免循环引用和内存泄漏。
  • 通过 weak_ptr.lock() 检查对象是否仍然有效,如果观察者已被销毁,将其从列表中移除。

2. 性能优化

  • 延迟通知:在有大量观察者时,通知所有观察者可能会带来性能开销。如果对实时性要求不高,可以引入批处理或者异步通知机制。可以使用 C++ 标准库中的 std::threadstd::async 来实现通知的并行或异步操作。

3. 增强可扩展性

  • 事件过滤:当前的观察者模式是所有观察者对每个通知都作出响应。可以通过在 notify 方法中增加条件逻辑,使观察者只对某些特定的事件类型作出响应,从而减少不必要的通知。例如,可以引入事件枚举,判断是否应该通知某个观察者。
enum class EventType {
    MESSAGE_UPDATE,
    DATA_UPDATE
};

class Observer {
public:
    virtual ~Observer() {}
    virtual void update(EventType type, const std::string& message) = 0;
};

class ConcreteSubject : public Subject {
    // 略...
    void notify(EventType type) override {
        for (auto observer : observers) {
            if (auto o = observer.lock()) {
                o->update(type, message);
            }
        }
    }
    
    // 可以根据不同的事件类型发送不同的通知
    void createMessage(const std::string& message) {
        this->message = message;
        notify(EventType::MESSAGE_UPDATE);
    }
};

改进要点

  • 添加事件类型判断,通知相关的观察者,避免不必要的更新,提升效率。

4. 线程安全

如果你的项目是多线程环境,考虑将 Subjectattach()detach()notify() 方法添加互斥锁以保证线程安全,避免多线程同时修改观察者列表可能带来的数据竞争。

可以使用 C++11 提供的 std::mutex 实现线程安全:

#include <mutex>

class ConcreteSubject : public Subject {
private:
    std::vector<std::weak_ptr<Observer>> observers;
    std::string message;
    std::mutex mtx;  // 互斥锁保证线程安全
public:
    void attach(std::shared_ptr<Observer> observer) override {
        std::lock_guard<std::mutex> lock(mtx);  // 加锁
        observers.push_back(observer);
    }

    void detach(std::shared_ptr<Observer> observer) override {
        std::lock_guard<std::mutex> lock(mtx);  // 加锁
        observers.erase(
            std::remove_if(observers.begin(), observers.end(), 
                           [&observer](const std::weak_ptr<Observer>& o) {
                               return o.lock() == observer;
                           }), 
            observers.end());
    }

    void notify() override {
        std::lock_guard<std::mutex> lock(mtx);  // 加锁
        for (auto it = observers.begin(); it != observers.end();) {
            if (auto observer = it->lock()) {
                observer->update(message);
                ++it;
            } else {
                it = observers.erase(it);
            }
        }
    }

    // 其他方法略...
};

改进要点

  • 引入互斥锁确保观察者列表的修改和遍历在多线程下的安全性。

5. 日志功能

可以为观察者模式中的 notify() 添加日志功能,记录每次的通知行为和状态变化。借助 std::ofstream 或其他日志库(如 spdlog),可以方便地跟踪整个系统的事件传播过程。

总结:

  • 使用 std::weak_ptr 避免循环引用。
  • 引入 事件类型 过滤以减少不必要的通知。
  • 增加 线程安全机制 以支持多线程环境。
  • 考虑异步或并行的 性能优化
  • 添加日志以增强 调试能力

通过这些改进,代码将变得更加健壮、可扩展且高效。

上一篇:【jQuery】jQuery 处理 Ajax 以及解决跨域问题的方式-HTTP


下一篇:【编程基础知识】《Java 基础之访问权限控制:解锁代码安全与封装的奥秘》