4. 观察者模式

发布于:2025-07-15 ⋅ 阅读:(20) ⋅ 点赞:(0)

一、现实应用场景

  1. 教师的手机号改变之后要通知给所有学生
  2. 如果有一个学生没有通知到位就会产生遗漏
  3. 如何自动完成

二、初步实现

2.1 实现方案1

  • 定义学生和教师类
  • 教师类设置set和get方法
  • 教师的手机号修改后要依次调用所有学生的类重新设置
#include <iostream>

class Student
{
private:
	std::string m_name;
	std::string m_tPhone;

public:
	Student(const std::string& name)
	{
		m_name = name;
	}

	void setTPhoneNubmer(const std::string& phone)
	{
		m_tPhone = phone;
	}

	void show()
	{
		std::cout << "Name: " << m_name << " Teacher's Phone: " << m_tPhone << std::endl;
	}
};

class Teacher
{
private:
	std::string m_phone;

public:
	Teacher(const std::string& phone)
	{
		m_phone = phone;
	}

	void setPhone(const std::string& phone)
	{
		m_phone = phone;
	}

	std::string getPhone() const 
	{
		return m_phone;
	}
};

int main()
{
	Teacher zwz("12345");
	Student li("LiLei");
	Student Jie("Jie");
	Student wcz("wcz");

	li.setTPhoneNubmer(zwz.getPhone());
	Jie.setTPhoneNubmer(zwz.getPhone());
	wcz.setTPhoneNubmer(zwz.getPhone());

	li.show();
	Jie.show();
	wcz.show();

	//修改
	zwz.setPhone("67890");
	li.setTPhoneNubmer(zwz.getPhone());
	Jie.setTPhoneNubmer(zwz.getPhone());
	wcz.setTPhoneNubmer(zwz.getPhone());

	li.show();
	Jie.show();
	wcz.show();

	return 0;
}

2.2 实现方案2

  • 学生类中拥有一个教师的实例,该实例由外部传入
  • 只要教师的手机号在外部修改,那个学生类中只需要通过getPhone()函数即可得到教师的手机号
  • 实现过程如下
#include <iostream>

class Teacher
{
private:
	std::string m_phone;

public:
	Teacher(const std::string& phone)
	{
		m_phone = phone;
	}

	void setPhone(const std::string& phone)
	{
		m_phone = phone;
	}

	std::string getPhone() const
	{
		return m_phone;
	}
};


class Student
{
private:
	std::string m_name;
	std::string m_tPhone;
	const Teacher* m_teacher;

public:
	Student(const std::string& name, const Teacher* teacher)
	{
		m_name = name;
		m_teacher = teacher;
	}

	void show()
	{
		std::cout << "Name: " << m_name << " Teacher's Phone: " << m_teacher->getPhone() << std::endl;
	}
};


int main()
{
	Teacher zwz("12345");
	Student li("LiLei", &zwz);
	Student Jie("Jie", &zwz);
	Student wcz("wcz", &zwz);


	li.show();
	Jie.show();
	wcz.show();

	//修改
	zwz.setPhone("67890");

	li.show();
	Jie.show();
	wcz.show();

	return 0;
}
  • 存在问题
    • 两个对象间存在紧耦合关系
    • 如果换老师之后,老师的实例还得修改
    • 缺少扩展性与灵活性

三、观察者模式

3.1 应用场景

  • 当对象发生变化,通知给其他对象,需要其他对象做出调整
  • 应用程序的可维护性和重用性较高
  • 互动关系不能体现成类之间的直接调用,对象之间关系的解耦

3.2 详解

  • 观察者模式又称为发布订阅模式
  • 两个角色: 观察者和被观察对象
  • 两者之间存在”观察“的逻辑关联
  • 当被观察者发生改变的时候,观察者就会观察到这样的变化,并且做出相应的响应
  • 观察不是直接调用
  • 实现观察者模式有很多形式,比较直观的一种是注册-> 通知 -> 撤销注册形式。

3.3 实现

  1. 步骤1:观察者将自己注册到被观察对象中,被观察对象将观察者存放在一个容器中。
  2. 步骤2:被观察者对象发生了某种变化,从容器中得到所有注册过的观察者,将变化通知观察者。
  3. 步骤3(可选):观察者告诉被观察者要撤销观察,被观察者从容器中将观察者去除。

3.4 设计类图

在这里插入图片描述

  • ConcreteSubject:
    • 主题对象,被观察者,对应老师。
    • 他有变化时通知ConcreteObserver类的实例(学生)。学生根据变化自动调用update()做出响应。
    • 内部有一个队列存储、移除观察者
    • 当状态发生变化时可以调用notifyObservers()通知观察者
  • ConcreteSubjectConcreteObserver类解耦
    • 学生不只可以观察老师,还可以观察学院、某个同学等。
    • 两个类分别向上抽取了被观察者接口 (Subject)和观察者接口(Observer)

四、实现

#include <iostream>
#include <list>

class IObserver
{
public:
	virtual void update(void* o) = 0;
};

class ISubject
{
public:
	virtual void registerObserver(IObserver* obj) = 0;
	virtual void removeObserver(IObserver* obj) = 0;
	virtual void notifyObserver() = 0;
};

class Teacher: ISubject
{
private:
	std::string m_phone;
	std::list<IObserver*> m_subject;

public:
	void setPhone(const std::string& phone)
	{
		m_phone = phone;
		notifyObserver();
	}

	std::string getPhone() const
	{
		return m_phone;
	}

	void registerObserver(IObserver* o)
	{
		m_subject.push_back(o);
	}

	void removeObserver(IObserver* o)
	{
		m_subject.remove(o);
	}

	void notifyObserver()
	{
		for (auto item : m_subject)
		{
			item->update((void*)m_phone.c_str());
		}
	}
};


class Student: public IObserver
{
private:
	std::string m_name;
	std::string m_tPhone;

public:
	Student(const std::string& name)
	{
		m_name = name;
	}

	void update(void* o)
	{
		m_tPhone = reinterpret_cast<char*>(o);
	}

	void show()
	{
		std::cout << "Name: " << m_name << " Teacher's Phone: " << m_tPhone << std::endl;
	}
};


int main()
{
	Teacher zwz;
	Student li("LiLei");
	Student Jie("Jie");
	Student wcz("wcz");
	Student test("test");

	zwz.registerObserver(&li);
	zwz.registerObserver(&Jie);
	zwz.registerObserver(&wcz);

	std::cout << "\n*******设置教师手机号为12345*******" << std::endl;
	zwz.setPhone("12345");

	li.show();
	Jie.show();
	wcz.show();

	zwz.removeObserver(&wcz); //移除对wcz的通知
	std::cout << "\n*******对教师手机号进行修改为67890*******" << std::endl;
	//修改
	zwz.setPhone("67890");


	li.show();
	Jie.show();

	std::cout << "\n*******不会被改变*******" << std::endl;
	wcz.show();  //不会被改变

	return 0;
}
  • 创建观察者接口IObserver(),并规定了更新的行为。
  • 创建被观察者接口ISubject(),并规定了添加、移除和通知观察者的行为。
  • 运行结果
    在这里插入图片描述

网站公告

今日签到

点亮在社区的每一天
去签到