05/08/2020
05/09/2020 大话设计模式中的观察者模式
观察者模式
回顾设计模式1
组合和继承
回顾上一个策略,鸭子类中有飞行行为和叫声行为,红鸭子,玩具鸭等其他玩具鸭子类继承鸭子类。
在这里有两个关键字
- 有一个:鸭子类有飞行行为,表示组合(composition)
- 是一个:红鸭子是鸭子,表示继承
有时候组合比继承更好
多用组合,少用继承。
策略模式- 模拟鸭子类
定义了算法族,分别封装起来,让他们之间可以互相替换,此模式让算法的变化独立于使用算法的客户
心得
- 面向对象的学习:在于如何复用,比如继承
OO基础
- 抽象
- 封装
- 多态
- 继承
OO原则
有一些面向对象原则,适用于所有的模式。但是不一定有适当的模式解决问题。
- 封装变化
- 多用组合,少用继承
- 针对接口编程,不针对实现编程
OO模式
- 策略模式
- 简单工厂
- 。。。
观察者模式(Observer)
观察者模式定义了对象之间一对多依赖,这样一来,当一个对象改变状态时,它的所有依赖者都会受到通知并自动更新。
例如:报纸订阅服务,有一个出版者对象对应多个订阅者对象。订阅者依赖出版者。出版者更新并通知给所有订阅者更新。
松耦合
当两个对象之间松耦合,它们依然可以交互,但是不太清楚彼此的细节。
设计原则4
为了交互对象之间的松耦合设计而努力。
案例:气象站与布告栏模拟
基本属性
气象站:
- 温度
- 湿度
- 大气压力
多个布告栏: - 根据气象站的信息,显式不同的状态
- 当前布告栏:显式温度,湿度,大气压力
- 天气预报布告栏:显式温度与湿度的占比多少,决定当前的天气状况
一对多关系,一对应Subject类,多对应Observer类. 它们之间相互拥有对方的类。
第一个问题
C++中如何解决相互引入头文件问题。
C++前向声明解决方案
主题接口与观察者接口
//interface
class Subject
{
public:
//主题可以增减通知观察者们
virtual void registerObserver(Observer o) =0;
virtual void removeObserver(Observer o) = 0;
virtual void notifyObservers() = 0;
};
//interface
class Observer //所有观察者继承这个接口,来实现update方法,来更新
{
public:
virtual void update(float temp, float humidity, float pressure) = 0;
};
//interface
class DisplayElement
{
public:
virtual void display() = 0;
};
继承主题接口的对象
class WeatherData: public Subject
{
public:
WeatherData()
{
observers = new std::list<Observer>
}
void registerObserver(Observer* o)override
{
observers.push_back(o);
}
void removeObserver(Observer* o)override
{
//for-loop to erase observer
}
void notifyObservers()override
{
for(auto& x: Observers)
{
x->update(mTemperator,mHumidity,mPressure);
}
}
void measurementsChanged()
{
notifyObservers();
}
void setMeasurements(float temperature, float humidity,
float pressure)
{
mTemperature = termperature;
mHumidity = humidity;
mPressure = pressure;
measurementsChanged();
}
private:
std::list<Observer*>* observers;
float mTemperature;
float mHumidity;
float mPressure;
};
布告栏继承观察接口
class CurrentConditionDisplay: public Observer,public DisplayElement
{
public:
CurrentConditionDisplay(Subject* weatherData)
{
this->weatherData = weatherData;
weatherData->registerObserver(this);
}
void update(float temperature, float humidity,
float pressure)override
{
mTemperature = temperature;
mHumidity = humidity;
mPressure = pressure;
display();
}
void display()override
{
//print private info.
}
private:
float mTemperature;
float mHumidity;
float mPressure;
Subject* weatherData; //有一个公共的主题对象
};
气象站运行
int main()
{
WeatherData* weatherData = new WeatherData; //主题
//观察者们
CurrentConditionDisplay* currentDisplay = new CurrentConditionDisplay(weatherData);
StatisticsDisplay* statisticsDisplay = new StatisticsDisplay(weatherData);
OtherDisplay* otherDisplay = new OtherDisplay(weatherData);
//通知观察者们
weatherData.setMeasurements(80.0,65.0,30.4);
weatherData.setMeasurements(23.4,53.7,30.4);
}
Java中自带的Observable
Observable并没有实现接口编程,而是把主题接口变成一个超类。
大话中的观察者模式
模拟关于老板通知问题
通常在学校上自习课的时候,坐在门口的同学如果发现老师来了,通常会通知同桌,继而教室也会变得安静许多。一旦老师走了,教室又会变得吵闹。老师来没来的状态与班级的状态相互联系。
双向耦合
class Teacher
{
public:
void attach(Student* student); //mStudentsList->push_back(student)
void notify()
{
for(auto x:mStudentsList)
{
x->update();
}
}
//get/set function for teacherAction
private:
std::list<Student*> mStudentsList;
std::string teacherAction; //老师的状态,来没来
};
class Student
{
public:
Student student(std::string name,Teacher* teacher):
mName(name),
mTeacher(teacher)
{}
void update()
{
std::cout << mName<<"停下讲话,去做作业"; //耦合所在,不是所有的学生都在讲话,有可能其他学生在听音乐
}
private:
std::string mName;
Teacher* mTeacher; //班主任相同
};
问题
- 这个是个双向耦合问题,有常量输出的地方就很难扩展,观察的同学不一定都在讲话,有可能在忙些别的事情,比如听音乐,睡觉等。
- 来的不一定是老师,如果换成校长也应该通知给所有的观察者们。
动机
- 将一个系统分割成一系列相互协作的类有一个很不好的副作用,那就是需要维护相关对象间的一致性。
- 一旦主题对象发生状态改变,其他观察者们都可以得到通知。
使用
- 当一个对象的改变需要同时改变其他对象的时候,并且主题对象不需要知道具体有多少对象有待改变
- 解除耦合。让耦合的双方都依赖于抽象,而不是依赖于具体。从而各自的变化不会影响另一边的变化。
- 依赖倒转原则的体现
Head First Design Pattern: Observer pattern
大话设计模式第14章