c++ PropertyChangeListener的实现

深渊向深渊呼唤

前言

    开发中经常有一个需求就是当某个对象甚至是某个模块的属性变化,或者是某个操作发生的时候需要设置一个监听器以通知出去,本身就是实现一个最简单够用的机制.

 

代码

PropertyChangeSupport.h



#pragma once

#include <boost/function.hpp>
#include <boost/any.hpp>
#include <vector>
#include <map>


typedef boost::function<void(std::string, boost::any, boost::any)> PropertyChangeListener;

class PropertyChangeSupport {
public:
    PropertyChangeSupport();

    ~PropertyChangeSupport();

    void addPropertyChangeListener(std::string name, PropertyChangeListener propertyChangeListener);

    void addPropertyChangeListener(PropertyChangeListener propertyChangeListener);

    virtual void
    firePropertyChange(std::string name, boost::any oldValue = nullptr, boost::any newValue = nullptr);

private:
    std::map<std::string, std::vector<PropertyChangeListener>> _pairListeners;
    std::vector<PropertyChangeListener> _listeners;
};

PropertyChangeSupport.cpp

PropertyChangeSupport::PropertyChangeSupport() {

}

PropertyChangeSupport::~PropertyChangeSupport() {

}

void PropertyChangeSupport::addPropertyChangeListener(std::string name, PropertyChangeListener propertyChangeListener) {
    auto pair = _pairListeners.find(name);
    if (pair != _pairListeners.end()) {
        pair->second.push_back(propertyChangeListener);
    } else {
        _pairListeners.insert({name, {propertyChangeListener}});
    }
}

void PropertyChangeSupport::addPropertyChangeListener(PropertyChangeListener propertyChangeListener) {
    _listeners.push_back(propertyChangeListener);
}

void PropertyChangeSupport::firePropertyChange(std::string name, boost::any oldValue, boost::any newValue) {
    for (int i = 0; i < _listeners.size(); ++i) {
        _listeners[i](name, oldValue, newValue);
    }
    auto pair = _pairListeners.find(name);
    if (pair != _pairListeners.end()) {
        auto v = pair->second;
        for (int i = 0; i < v.size(); ++i) {
            v[i](name, oldValue, newValue);
        }
    }
}

实现原理非常简单,设置一个回调函数,根据不同的propertyname调用对应的函数对象,使用boost::any来做值的包装.由于监听的方法是在同一个线程执行,所以如果监听方法代码不当就会导致失败.

测试代码

class Bean : public PropertyChangeSupport {

public:
    Bean() {

    }

    void setA(int v) {
        auto old = a;
        a = v;
        firePropertyChange("a", old, v);
    }

    void setB(std::string v) {
        auto old = b;
        b = v;
        firePropertyChange("b", old, b);
    }

    int a;
    std::string b;
};

int main() {
    Bean b;
    b.addPropertyChangeListener("b", [](std::string name, boost::any oldV, boost::any newV) {
        LOG_INFO << name <<  boost::any_cast<std::string>(oldV) <<  boost::any_cast<std::string>(newV);
    });
    b.addPropertyChangeListener("a", [](std::string name, boost::any oldV, boost::any newV) {
        LOG_INFO << name <<  boost::any_cast<int>(oldV) <<  boost::any_cast<int>(newV);
    });
    b.addPropertyChangeListener([](std::string name, boost::any oldV, boost::any newV) {
        LOG_INFO << name;
    });
    b.setB("sss");
    b.setA(111);
    b.setA(222);
}

 

栏目