设计模式之观察者模式

设计模式专栏:http://t.csdnimg.cn/4Mt4u 

面向对象设计原则

接口隔离原则面向对象设计之接口隔离原则-CSDN博客

设计模式

工厂模式 : 设计模式之工厂模式-CSDN博客

迭代器模式设计模式之迭代器模式-CSDN博客

适配器模式设计模式之适配器模式-CSDN博客

过滤器模式设计模式之过滤器模式-CSDN博客

单例模式设计模式之单例模式-CSDN博客

观察者模式设计模式之观察者模式-CSDN博客

空对象模式设计模式之空对象模式-CSDN博客

责任链模式设计模式之责任链模式-CSDN博客

策略模式设计模式之策略模式-CSDN博客

Pimpl技法C++之Pimpl惯用法-CSDN博客

组合模式设计模式之组合模式-CSDN博客

桥接模式设计模式之桥接模式-CSDN博客

目录

1.简介

2.结构图

3.具体实现

4.优缺点

5.使用场景


1.简介

观察者模式是我们日常用的比较多的一种行为设计模式,有时又被称为发布Publish-订阅Subscribe模式、模型Model-视图View模式、源-收听者Listener模式或从属者模式;在GOF的《设计模式:可复用面向对象软件的基础》一书中对观察者模式是这样说的:定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并被自动更新。当一个对象发生了变化,关注它的对象就会得到通知;目标是通知的发布者,它发出通知时并不需要知道谁是它的观察者。

2.结构图

观察者模式UML类图如下:

Subject(目标):定义被观察者必须实现的职责,它必须能够动态地增加、取消观察者。它一般是抽象类或者是实现类,仅仅完成作为被观察者必须实现的职责,管理观察者并通知观察者。

Observer(观察者): 为那些在目标发生改变时需获得通知的对象定义一个更新接口。

ConcreteSubject(具体目标):将有关状态存入各ConcreteObserver对象;定义被观察者自己的业务逻辑,同时定义对哪些事件进行通知。

ConcreteObserver(具体观察者):存储有关状态,这些状态应与目标的状态保持一致;维护一个指向ConcreteSubject对象的引用;每个观察在接收到消息后的处理反应是不同,各个观察者有自己的处理逻辑。

观察者模式通过将主题和观察者解耦,实现了对象之间的松耦合。当主题的状态发生改变时,所有依赖于它的观察者都会收到通知并进行相应的更新。

3.具体实现

主题和观察者实现如下:

class IObserver;

//抽象主题
class ISubject
{
public:
	virtual ~ISubject() {}
	virtual void add(IObserver* observer) = 0;
	virtual void remove(IObserver* observer) = 0;
	virtual void notify() = 0;

	virtual int getState() const = 0;
	virtual void setState(int state) = 0;
};

//抽象观察者
class IObserver
{
public:
	virtual ~IObserver() {}
	virtual void update(ISubject* pSubject) = 0;
};

//具体主题
class CConcreteSubject : public ISubject
{
public:
	void add(IObserver* observer)  override {
		m_vecObservers.push_back(observer);
	}
	void remove(IObserver* observer) override {
		m_vecObservers.remove(observer);
	}
	void notify() {
		for (auto& it : m_vecObservers) {
			it->update(this);
		}
	}
	int getState() const override {
		return m_state;
	}
	void setState(int state) override {
		m_state = state;
		notify();
	}
private:
	std::list<IObserver*> m_vecObservers;
	int m_state;
};

//具体观察者1
class CConcreteObserver1 : public IObserver
{
public:
	void update(ISubject* pSubject) override {
		std::cout << "CConcreteObserver1 recv update, New value: " << std::to_string(pSubject->getState());
	}
};

//具体观察者2
class CConcreteObserver2 : public IObserver
{
public:
	void update(ISubject* pSubject) override {
		std::cout << "CConcreteObserver2 recv update,New value:" << std::to_string(pSubject->getState());
	}
};

//具体观察者3
class CConcreteObserver3 : public IObserver
{
public:
	void update(ISubject* pSubject) override {
		std::cout << "CConcreteObserver3 recv update,New value:" << std::to_string(pSubject->getState());
	}
};

测试程序如下:

int  main()
{
    // Create Subject
	ISubject* pSubject = new CConcreteSubject();

	// Create Observer
	IObserver* pObserver1 = new CConcreteObserver1();
	IObserver* pObserver2 = new CConcreteObserver2();
	IObserver* pObserver3 = new CConcreteObserver3();

	pSubject->add(pObserver1);
	pSubject->add(pObserver2);
	pSubject->add(pObserver3);

	// Change the state
	pSubject->setState(2);

	// Unregister the observer
	pSubject->remove(pObserver1);

	// Change the state
	pSubject->setState(10);

	// Unregister the observer
	pSubject->remove(pObserver2);

	pSubject->setState(3);

	delete pObserver1;
	delete pObserver2;
	delete pObserver3;
	delete pSubject;
    
    return 0;
}

输出:

CConcreteObserver1 recv update, New value: 2
CConcreteObserver2 recv update, New value: 2
CConcreteObserver3 recv update, New value: 2

CConcreteObserver2 recv update, New value: 10
CConcreteObserver3 recv update, New value: 10

CConcreteObserver3 recv update, New value: 3

        这个例子很简单,是一种经典的观察者模式实现,但这种实现不够通用,只能对特定的观察者才有效,即必须是 Obsrver 抽象类的派类才行,并且这个观察者类还不能带参数,虽然能在抽象类中定义带几个指定参数的观察方法,但这仍然不够用,因为在实际情况下参数个数是不定的。这种实现方式限定太多,主要的两个限定是:第一,需要继承,继承是强对象关系,不够灵活,第二,观察者被通知的接口参数不支持变化,导致观察者不能应付接口的变化。

        我们可以通过 C++11 做一些改进,主要改进的地方有两个: 通过被通知接口参数化和std::function 来代替继承;通过可变参数模板和完美转发来消除接口变化产生的影响。改进之后的观察者模式和 C# 中的 event 类似,通过定义委托类型来限定观者者,不要求观察者必须从某个类派生,当需要和原来不同的观察者时,只需要定义一个新的 event 类型即可,通过event还可以方便地增加或者移除观察者。
        我们还希望这个 event 类不可复制,要使类成为不可复制的,典型的实现方法是将类的复制构造丽数和赋值运算符设置为 private 或 proteted。如果二者都定义,那么编译器会提供一种作为公共成员函数的隐式版本。C++11 提供了 Defaut 和 Delete 函数,使我们可以更方便地实现一个NonCopyable 了,如果希望类不被复制,则直接从这个 NonCopyable 派生即可。NonCopyable 的实现如代码:

class NonCopyable
{
protected:
    NonCopyable() = default;
    ~NonCopyable()=default;
    NonCopyable(const NonCopyable&)=delete;//禁用复制构造//禁用赋值构造
    NonCopyable& operator= (const NonCopyable&) = delete;
}

#include <iostream>
#include <vector>
#include <map>
#include <functional>
#include <string>
using namespace std;
 
template<typename Func>
class Events
{
public:
    Events(){};
 
    ~Events(){};
 
    //注册观察者,支持右值引用
    int Connect(Func&& f)
    {
        return Assign(f);
    }
 
    //注册观察者
    int Connect(const Func& f)
    {
        return Assign(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)...);
        }
    }
 
private:
    ///给观察者分配编号,在多线程环境需要加锁
    template<typename F>
    int Assign(F&& f)
    {
        int k = m_observerId++;
        m_connections.emplace(k, std::forward<F>(f));
        return k;
    }
 
    int m_observerId = 0;
    std::map<int, Func> m_connections;
};

测试代码:

void print(int a, int b)
{
    cout << "func " <<  a << ", " << b << endl;
}
 
class MyTest
{
public:
    void print(int a, int b)
    {
        cout << "class " << a << ", " << b << endl;
    }
};
 
int main()
{
    Events<std::function<void(int, int)>> myevent;
 
    auto key = myevent.Connect(print);
 
    MyTest a;
 
    std::function<void(int, int)> f = std::bind(&MyTest::print, &a, std::placeholders::_1, std::placeholders::_1);
 
    auto key1 = myevent.Connect(f);
 
    ///lambda注册
    auto lamkey = myevent.Connect([](int a, int b){cout << "lambda  " << a << ", " << b << endl;});
 
    myevent.Notify(1, 2);
 
    return 0;
}

C++11实现的观察者模式,内部维护了一个泛型函数列表,观察者只需要将观察者函数注册进来即可,消除了继承导致的强耦合。通知接口使用了可变参数模板,支持任意参数,这就消除了接口变化的影响。

4.优缺点

优点:

1)降低耦合度:观察者模式通过将主题和观察者之间的合关系抽象化为一种依赖关系,使得主题和观察者之间的耦合度降低,提高了代码的可维护性和复用性。
2)自动通知:观察者模式可以自动通知所有关联的观察者对象,避免了手动更新和回调的繁锁,提高了开发效率。
3)支持广播通信:观察者模式支持广播通信,即主题会将通知发送给所有关联的观察者,无论它们是否关心主题的状态变化;这有助于减少不必要的通信和计算。

缺点:
1)通知可能过于频繁:观察者模式可能会导致过多的通知,特别是在主题状态频繁变化的情况下,这会增加观察者的处理负担和系统的负担。
2)通知可能不准确:观察者模式没有提供一种机制来保证所有观察者都能及时收到通知,因此可能会出现通知不准确的情况。例如,在某些情况下,一些观察者可能无法收到通知。
3)可能存在循环引用:在桌些情况下,观察者和主题之间可能存在循环引用,导致无法正常解耦。例如,一个观察者依赖主题赖的状态,而主题又依赖该观察者的行为
4)可能存在内存泄漏:如果观察著或主题对象持有集他对象的引用可能会导致内存泄漏。例如,一个观察者持有一个大对象的引用,而该对象又被其他对象引用,这样就会形成一个引用链,从而导致内存池漏。
5)观察者模式需要考虑一下开发效率和运行效率问题,一个被观察者,多个观察者,开发和调试就会比较复杂,而且在Java中消息的通知默认是顺序执行,一个观察者卡壳,会影响整体的执行效率。在这种情况下,一般考虑采用异步的方式。多级触发时的效率更是让人担忧,大家在设计时注意考虑。

5.使用场景

  • 一个抽象模型有两个方面,其中一个方面依赖于另一个方面。将这些方面封装在独立的对象中使它们可以各自独立地改变和复用。
  • 一个对象的改变将导致其他一个或多个对象也发生改变,而不知道具体有多少对象将发生改变,可以降低对象之间的耦合度。
  • 一个对象必须通知其他对象,而并不知道这些对象是谁。
  • 需要在系统中创建一个触发链,A对象的行为将影响B对象,B对象的行为将影响C对象……,可以使用观察者模式创建一种链式触发机制。
  • 23
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值