在观察者模式之前突然又重新想了一下,有了些新的理解。如有不正,请指出。
一、上一篇重思考
关于这个“变化”,个人觉得,在Duck类中,Duck变化的部分有很多,如果分为属性和行为的话,属性是变化的,比如鸭子颜色、体重;行为也是变化的,比如鸭子的飞行方式,但是这两个变化有些不一样的,比如,鸭子的体重,体重的数值是变化的,但是体重这个属性的类型,无论是怎么变化的,它的类型一般都会认为是浮点型数据,所以,构造了Duck超类,在超类中定义体重属性,并定义这个属性是浮点型;而飞行行为这个行为的变化实际上是函数的变化,这个我个人对此的理解是,java里没指针,当函数实现方式变化的时候,我们需要一个同一的“接口”用于描述这个变化属性的类型,就比如体重这个属性变化,但永远是浮点型,而fly这个函数行为变化,通过一个超类,也就是flyBehavior这个Interface去描述fly这个函数的类型,也就是论fly函数内部怎么实现,永远都是flyBehavior这个类型;而java之所以这样做的根本原因是没有指针,在C++里,函数无论内部怎么实现,只要函数返回值类型,形参类型不变,就可以用一个函数指针表示,至于这个指针指向哪个函数,我们可以动态绑定,就是说,上一篇代码完全没必要按照java的逻辑写一个类,直接可以通过函数指针事现函数行为的变化(毕竟虚函数的重写也要保证函数返回值,名称、形参列表一样,完全可以用函数指针代替),所以这里先放一个函数指针实现的代码。
QuackBehavior.h
// QuackBehavior.h
#pragma once
void Quack();
void MuteQuack();
void Squeak();
QuackBehavior.cpp
// QuackBehavior.cpp
#include <iostream>
#include "QuackBehavior.h"
using std::cout;
using std::endl;
void Quack() {
cout << "Quack" << endl;
}
void MuteQuack() {
cout << "<<Slience>>" << endl;
}
void Squeak() {
cout << "Squeak" << endl;
}
FlyBehavior.h
// FlyBehavior.h
#pragma once
void FlyWithWngs();
void FlyNoWay();
FlyBehavior.cpp
// FlyBehavior.cpp
using std::cout;
using std::endl;
void FlyWithWngs() {
cout << "I'm flying!" << endl;
}
void FlyNoWay() {
cout << "I can't fly" << endl;
}
Duck.h
// Duck.h
#pragma once
class Duck {
public:
typedef void(*flyCallback)();
typedef void(*quackCallback)();
Duck() {}
void display();
void perfromFly();
void performQuack();
void swim();
void setFlyBehavior(flyCallback fb);
void setQuackBehavior(quackCallback gb);
~Duck() {}
private:
flyCallback fly;
quackCallback quack;
};
Duck.cpp
#include <iostream>
#include "Duck.h"
using std::cout;
using std::endl;
void Duck::display() {
cout << "call function display" << endl;
}
void Duck::perfromFly() {
if (fly) {
fly();
}
}
void Duck::performQuack() {
if (quack) {
quack();
}
}
void Duck::swim() {
cout << "All ducks float, even decoys!" << endl;
}
void Duck::setFlyBehavior(flyCallback fb) {
fly = fb;
}
void Duck::setQuackBehavior(quackCallback gb) {
quack = gb;
}
main.cpp
#include "QuackBehavior.h"
#include "FlyBehavior.h"
#include "Duck.h"
int main() {
Duck* MallardDuck = new Duck();
MallardDuck->setFlyBehavior(FlyWithWngs);
MallardDuck->setQuackBehavior(Quack);
MallardDuck->perfromFly();
MallardDuck->performQuack();
MallardDuck->setFlyBehavior(FlyNoWay);
MallardDuck->setQuackBehavior(MuteQuack);
MallardDuck->perfromFly();
MallardDuck->performQuack();
MallardDuck->setQuackBehavior(Squeak);
MallardDuck->perfromFly();
MallardDuck->performQuack();
delete MallardDuck;
return 0;
}
输出结果和原来一样
I'm flying!
Quack
I can't fly
<<Slience>>
I can't fly
Squeak
总结来说:属性变化,比如属性的数值变化,可以用一个内置类型的变量;函数行为变化,比如函数内部实现方式变化,可以用函数指针;如果一个类变化,比如类的成员函数和成员变量发生很大的变化,可以用一个超类表示些类的变量。或者变化的这三个表明了变化的程度,比如体重,我们认为是浮点型,不会变成字符串,所以用一个变量;函数实现,只是内部实现,返回类型,参数列表不会变,所以用函数指针;类,内部函数组合巨变,或者把前面变化的东西封装在一起,可以用一个超类继承来解决。
二、观察者模式
观察者模式,让你的对象知道你的现状,没有对象咋办,不知道,我也没有.....
书中是从气象站开始的,咱们也从气象站开始,有这么一个需求,就是此系统中的三个部分是气象站(获取实际气象数据的物理装置)、WeatherData对象(追踪来自气象站的数据,并更新布告板)和布告板(显示目前天气状况给用户看)。
那什么是观察者模式呢?观察者模式定义了对象之间一对多依赖,这样一来,当一个对象改变状态时,它的所有依赖者都会收到通知并自动更新()。
观察者模式提供了一种让主题和观察者之间的松耦合,为什么呢,因为主题不需要知道观察者是什么,只要任何一个实现了Observer接口的东西都可以作为观察者。主题只使用那些接口,告知自己的状态,至于观察者看到这些状态做什么,和主题没有关系。
设计原则:为了交互对象之间的松耦合设计而努力。松耦合能够应对变化,因为对象之间的互相依赖降到了最低,当一部分变化时,而不会影响其余代码。
三、代码
1.书上java版本
public interface Subject{
public void registerObserver(Observer o);
public void removeObserver(Observer o);
public void notifyObserver();
}
public interfeace Observer{
public void update(float temp, float humidity, float pressure);
}
public interface DisplayElement{
public void display();
}
public class WeatherData implements Subject{
private ArrayList observers;
private float temperature;
private float humidity;
private float pressure;
public WeatherData(){
observers = new ArrayList();
}
public void removeObserver(Observer o){
int i = observers.indexof(o);
if(i >= 0){
observer.remove(i);
}
}
public void notifyObservers(){
for(int i = 0; i < observers.size(); ++i){
Observer observer = (Observer)observers.get(i);
observer.update(temperature, humidity, pressure);
}
}
public void setMeasurements(float temperature, float humidity, float pressure){
this.temperature = temperature;
this.pressure = pressure;
this.humidity = humidity;
measurementsChanged();
}
}
public class CurrentConditionDisplay implements Observer, DisplayElement{
private float temperature;
private float humidity;
private Subject weatherData;
public CurrentCondtionDisplay(Subject weatherData){
this.weatherData = weatherData;
weatherData.registerObserver(this);
}
public void update(float temperature, float humidity, float pressure){
this.temperature = temperature;
this.humidity = humidity;
display();
}
public void display(){
System.out.println("Current conditions:" + temperature + "F degrees and " + humidity + "% humidity");
}
}
简单的测试程序
public class WeatherStation{
public static void main(String[] args){
WeatherData weatherdata = new WeatherData();
CurrentConditionsDisplay currentDisplay = new CurrentConditionsDisplay(weatherData);
weatherData.setMeasurements(80, 65, 30.4f);
}
}
不知道上面的代码能运行吗,没有java,没试过,java推荐看书中的代码。
2.C++版本
SubjectInterface.h
// SubjectInterface.h
#pragma once
class Subject {
public:
virtual void registerObserver(Observer* o) = 0;
virtual void removeObserver(Observer* o) = 0;
virtual void notifyObservers() = 0;
virtual ~Subject() {};
};
ObserverInterface.h
// ObserverInterface.h
#pragma once
#include <string>
#include <iostream>
class Observer abstract{
public:
virtual void update(float temp, float humidity, float pressure) = 0;
virtual ~Observer() {}
void setName(std::string& s) { name = s; }
std::string getName() { return name; }
void printName() { std::cout << "I'am " << name << std::endl; }
private:
std::string name;
};
displayElement.h
// displayElement.h
#pragma once
class displayElement {
public:
virtual void display() = 0;
virtual ~displayElement() {}
};
SubjectImplements.h
// SubjectImplements.h
#pragma once
#include <set>
#include "SubjectInterface.h"
class WeatherData : public Subject{
public:
WeatherData() : temperature(0), humidity(0), pressure(0), observers() {}
virtual void registerObserver(Observer* o) final;
virtual void removeObserver(Observer* o) final;
virtual void notifyObservers() final;
void setMeasurements(float temp, float hmdt, float press);
void measurementChange();
float getTemperature();
float getHumidity();
float getPressure();
virtual ~WeatherData() {}
private:
std::set<Observer*> observers;
float temperature;
float humidity;
float pressure;
};
SubjectImplements.cpp
// SubjectImplements.cpp
#include <algorithm>
#include "ObserverInterface.h"
#include "SubjectImplements.h"
void WeatherData::registerObserver(Observer* o) {
if (observers.find(o) == observers.end()) {
observers.insert(o);
}
}
void WeatherData::removeObserver(Observer* o) {
if (observers.find(o) != observers.end()) {
observers.erase(o);
}
}
void WeatherData::notifyObservers() {
for (auto it : observers) {
it->update(temperature, humidity, pressure);
}
}
void WeatherData::setMeasurements(float temp, float hmdt, float press) {
this->temperature = temp;
this->humidity = humidity;
this->pressure = press;
measurementChange();
}
void WeatherData::measurementChange() {
notifyObservers();
}
float WeatherData::getTemperature() {
return temperature;
}
float WeatherData::getHumidity() {
return humidity;
}
float WeatherData::getPressure() {
return pressure;
}
ObserverImplements.h
// ObserverImplements.h
#pragma once
#include <string>
#include "ObserverInterface.h"
#include "displayElement.h"
class CurrentConditionDisplay : public Observer, public displayElement {
public:
CurrentConditionDisplay(std::string n) : temperature(0), humidity(0), pressure(0) { setName(n); }
virtual void update(float temp, float humidity, float pressure) override final;
virtual void display() override final;
virtual ~CurrentConditionDisplay() {}
private:
float temperature;
float humidity;
float pressure;
};
ObserverImplements.cpp
// ObserverImplements.cpp
#include <iostream>
#include "ObserverImplements.h"
using std::cout;
using std::endl;
void CurrentConditionDisplay::update(float temp, float humidity, float pressure) {
this->temperature = temp;
this->humidity = humidity;
this->pressure = pressure;
display();
}
void CurrentConditionDisplay::display() {
printName();
cout << "Current conditions: " << temperature << "F degrees and " << humidity << "% humidity" << endl;
}
main.cpp
#include "ObserverImplements.h"
#include "SubjectImplements.h"
//#define _CRTDBG_MAP_ALLOC
//#include <stdlib.h>
//#include <crtdbg.h>
int main() {
//_CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF);
WeatherData* weatherdata = new WeatherData();
Observer* obs1 = new CurrentConditionDisplay("obs1");
Observer* obs2 = new CurrentConditionDisplay("obs2");
weatherdata->registerObserver(obs1);
weatherdata->registerObserver(obs2);
weatherdata->setMeasurements(80, 65, 30.4f);
weatherdata->removeObserver(obs1);
weatherdata->removeObserver(obs1);
delete weatherdata;
delete obs1;
delete obs2;
return 0;
}
注释部分是用于vs内存泄漏的一个简单检测代码
四、总结
观察者模式似乎没有什么好说的,毕竟,我也没有工程经验,学习cpp的小白,所以,也说不出啥,倒是有对比观察者模式和发布-订阅模式对比,这个观察者模式 vs 发布订阅模式下面的评论讲的也挺好的,毕竟没实际使用过解决问题,确实只知道基本的东西,不过,最近开始看《linux多线程服务端编程:使用muduo C++网络库》,这本书是陈硕大佬写的,第一章在写多线程对象的生命期管理的时候用的示例恰好也是观察者的一个框架,这里挖个坑,有时间写个那本书的读书笔记。不过我记得muduo源码中有一部分架构和观察者模式一样,但是好像并没有采用上面那种写法,而是更偏向于函数指针实现,等下次再比对一下怎样才能实现线程安全(狗头保命)。