在写程序的过程中,我们肯定有过一个问题:我有两个或多个对象,当其中的某个对象中的值改变时,如何让其它对象知道并且获取这个最新的值。下面就以外卖点餐为例,简要介绍如何实现一个观察者模式进行对象之间的通信。本文主要参考《Head First 设计模式》。

所谓观察者模式,就是建立对象间一对多的依赖关系,所有依赖于它(称为主题 Subject)的对象(称为观察者 Observer),都能自动更新(获取)到它的最新状态。比如我们有一家外卖店(mystore),当客户(client)在该店点餐后每隔一段时间需要知道骑手所在的位置,这就需要我们的店对客户进行通知,以更新骑手位置的最新状态。这时,外卖店和客户之间就是一种一对多的依赖关系,见下图。

首先,客户作为观察者需要有一个观察的目标,即我需要绑定哪一家店的以获取动态,而外卖店之所以能通知到每个客户是因为其内部保存了一个客户的链表,当有状态更新时需要通知哪些客户。因此,外卖店作为主题需要具备以下三个接口:

  1. 注册观察者 —— registerObserver
  2. 移除观察者 —— removeObserver 
  3. 通知观察者 —— notifyObservers

当有客户来时需要注册客户信息,当有状态更新时能够通知到客户,当有客户不想继续关注该店时能够移除客户。为此设计一下接口Subject:

// 抽象类
class Subject
{
public:
	Subject() {};
	virtual ~Subject() {};
	virtual void registerObserver(Observer* o) = 0;		// 注册
	virtual void removeObserver(Observer* o) = 0;		// 移除
	virtual void notifyObservers() = 0;	                // 通知
};

我们的外卖店则是继承该抽象类,重写这三个接口:

//.h

// 我的小店
class myStore : public Subject
{
public:
	myStore();
	~myStore();

	// 重写 Subject 的接口
	void registerObserver(Observer* o);
	void removeObserver(Observer* o);
	void notifyObservers();

	// 子类的方法
	void measurementsChanged();
	void setMeasurements(std::string location);

private:
	std::list<Observer*> m_Observers;	// 观察者
	std::string m_Location;
};


//.cpp
myStore::myStore()
{

}

myStore::~myStore()
{
	list<Observer*>::iterator iter = m_Observers.begin();
	while (iter != m_Observers.end())
	{
		delete (*iter);
		++iter;
	}
}

void myStore::registerObserver(Observer* o)
{
	if (o == nullptr)
	{
		cout << "Observer not exist!";
		return;
	}
	else
	{   // 这里需要判一下 o 是否存在
		m_Observers.push_back(o);
	}
}

void myStore::removeObserver(Observer* o)
{
	list<Observer*>::iterator iter = m_Observers.begin();
	while (iter != m_Observers.end())
	{
		if (*iter == o)
		{
			iter = m_Observers.erase(iter);    // erase返回当前元素的下一个位置
		}
		else
		{
			++iter;
		}	
	}
}

void myStore::notifyObservers()
{
	list<Observer*>::iterator iter = m_Observers.begin();
	for (; iter != m_Observers.end(); iter++)
	{
		(*iter)->update(m_Location);
	}
}

void myStore::measurementsChanged()
{
	notifyObservers();
}

void myStore::setMeasurements(string location)
{
	m_Location = location;
	measurementsChanged();
}

每当有新的状态时(measurementsChanged()),就通知客户并更新(update())。外卖店的状态通过setMeasurements()进行设置。

客户作为观察者的主要接口为update(),该接口负责更新状态并显示信息。观察者的基类如下:

// 观察者接口
class Observer
{
public:
	Observer() {};
	virtual ~Observer() {};
	virtual void update(std::string location) = 0;
};

下面是我们的客户。我们的客户需要绑定一个主题,因此在构造函数中需要传入一个主题对象的指针。

//.h
// 客户1
class Client1 : public Observer
{
public:
	Client1(Subject* subject);
	~Client1();

	void update(std::string location);	// 重写
	void display();

private:
	Subject* m_store;		// 店铺
	std::string m_Location;
};

//.cpp
Client1::Client1(Subject* subject)
{
	m_store = subject;
	subject->registerObserver(this);	// 注册主题
}

Client1::~Client1()
{
	delete m_store;
}

void Client1::update(string location)
{
	m_Location = location;

	display();
}

void Client1::display()
{
	SYSTEMTIME sys;
	GetLocalTime(&sys);
	cout << "Client1:" << endl;
	cout << "Current time " << sys.wHour << ":" << sys.wMinute << ":" << sys.wSecond << endl;
	cout << m_Location << endl;
}

观察者的类图:

 

如何使用:

int main()
{
	myStore *store = new myStore();		
	Client1* client1 = new Client1(store);	// 客户(观察者)1,每一个客户多多少少都要关注一家店铺(主题)
	Client2* client2 = new Client2(store);	// 客户(观察者)2,每一个客户多多少少都要关注一家店铺(主题)
	store->setMeasurements("骑手已接单");
	Sleep(5000);
	store->setMeasurements("骑手已到天桥");
}

 

运行结果

最后需要注意的是,观察者模式尽量建立抽象类之间的依赖关系。

Logo

北京人形旗下天工造物具身智能开源社区,聚焦具身天工与慧思开物两大平台

更多推荐