(原創)c++11改進我們的模式之改進觀察者模式


和單例模式面臨的是同樣的問題,主題更新的接口難以統一,很難做出一個通用的觀察者模式,還是用到可變模板參數解決這個問題,其次還用到了右值引用,避免多余的內存移動。c++11版本的觀察者模式支持注冊的觀察者為函數、函數對象和lamda表達式,也避免了虛函數調用,更簡潔更通用。直接看代碼。

template<typename Func>
class Events
{
public:
	Events():m_nextKey(0)
	{}
	~Events(){}

int Connect(Func&& f)
{
return Assgin(f);
}

int Connect(Func& f)
{
return Assgin(f);
}

template<typename F>
int Assgin(F&& f)
{
int k=m_nextKey++;
m_connections[k]=f;
return k;
}

	void Disconnect(int key)
     { m_connections.erase(key); } template<typename... Args> void Notify(Args&&... args) { for (auto& it: m_connections) { it.second(std::forward<Args>(args)...); } } private: int m_nextKey; std::map<int, Func> m_connections; };

 

測試代碼:

struct stA { int a, b; };
void print(int a, int b) { cout << a << ", " << b << endl; }
void TestObserver()
{
	Events<std::function<void(int,int)>> myevent;

	auto key=myevent.Connect(print); //以函數方式注冊觀察者
	stA t;
	auto lambdakey=myevent.Connect([&t](int a, int b){ t.a=a; t.b=b; });//lamda注冊

	int a=1,b=2;
	myevent.Notify(a,b); //廣播所有觀察者
	myevent.Disconnect(key); //移除觀察者
	
}

 /**********************更新,增加+=和-=運算符重載,使用法和c#的event的用法接近。

#include <map>
template<typename Func>
class Events : NonCopyable
{
public:
    Events() :m_nextKey(0)
    {}
    ~Events(){}
    

    int operator += (Func&& f)
    {
        return Connect(std::forward<Func>(f));
    }

    int operator += (Func& f)
    {
        return Connect(f);
    }

    template<typename... Args>
    void operator()(Args&&... args)
    {
        Notify(std::forward<Args>(args)...);
    }

    Events& operator -= (int key)
    {
        Disconnect(key);
        return *this;
    }

    void Clear()
    {
        m_connections.clear();
    }

private:
    int Connect(Func&& f)
    {
        return Assgin(std::forward<Func>(f));
    }

    int Connect(Func& f)
    {
        return Assgin(f);
    }

    void Disconnect(int key)
    {
        m_connections.erase(key);
    }

    template<typename... Args>
    void Notify(Args&&... args)
    {
        for (auto& it : m_connections)
        {
            it.second(std::forward<Args>(args)...);
        }
    }

    template<typename F>
    int Assgin(F&& f)
    {
        int index = m_nextKey++;
        Push(index, std::forward<Func>(f));
        return index;
    }

    template<typename F>
    void Push(int index, F&& f)
    {
        m_connections.emplace(index, std::move(f));
    }

    template<typename F>
    void Push(int index, F& f)
    {
        m_connections.emplace(index, f);
    }

private:
    int m_nextKey;
    std::map<int, Func> m_connections;
};

測試代碼:

struct stA { 
    int a;
    int b;
    void fun(int x, int y)
    {
        a = x;
        b = y;
        cout << "a = " << a << " b= " << b << endl;
    }
};
void print(int a, int b) { cout << a << ", " << b << endl; }
void TestEvent()
{
    using Delegate1 = std::function<void(int, int)>;
    using Event1 = Events<Delegate1>;
    Event1 evnet1;
  //添加委托 stA t; auto key1
= evnet1 += &print; auto key2 = evnet1 += [&t](int a, int b){ t.a = a; t.b = b; cout << "t.a = " << t.a << " t.b = " << t.b << endl; }; auto key3 = evnet1 += std::bind(&stA::fun, &t, std::placeholders::_1, std::placeholders::_2);
  //廣播 evnet1(
2, 3);
  //移除委托 evnet1
-= key1; evnet1 -= key2;
evnet1(
4, 5);
  //清空事件 evnet1.Clear(); evnet1(
1, 2);//清空什么都不會輸出 }

輸出結果:

  在第一個版本的基礎上增加了+=和-=運算符,使用法更接近c#,這里+=會返回一個key,這個key用來-=刪除委托時用到,這種做法不太好,只是一個簡單的處理。如果內部用vector的話,-=時,根據function去刪除指定的委托的話,用法就和c#完全一致了,不過,這里遇到的問題是function不支持比較操作,導致將function存入容器后,后面再根據function去刪除時就找不到對應的function了。還沒有足夠的時間去研究這個問題,留到后面再想辦法解決function比較的問題。

如果讀者有更好的解決辦法不妨提出來,討論一下。

 

c++11 boost技術交流群:296561497,歡迎大家來交流技術。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM