当前位置: 首页 > 后端技术 > PHP

“HEAD-FIRST”观察者模式

时间:2023-03-30 00:28:54 PHP

这是一系列的设计模式。本书所有案例均来自《Head-FirstDesignPattern(中文版)》,Github地址,欢迎大家围观,star观察者模式定义了对象之间的一对多依赖。当一个对象的状态发生改变时,它的所有依赖者都会收到通知并自动更新。观察者模式描述了图表设计难题。有一个气象台,我们想搭建一个应用,有三个布告栏(用来显示不同的天气数据),当气象站获取到最新的测量数据时,我们希望三个布告栏可以实时更新。在类图设计中,WeatherData用于获取气象站的最新测量数据(三种get方法)。当数据更新时,会调用onChanged方法(先不管为什么,这是气象站的内部逻辑)。代码实现主题界面interfaceSub{publicfunctionregisterObserver(Observer$observer);公共功能removeObserver();publicfunctionnitifyObservers();}主题对象WeatherDataclassWeatherDataimplementsSublect{protected$observers=[];保护$压力,$温度,$湿度;publicfunctionregisterObserver(Observer$observer){if(array_search($observer,$this->observers)===false){$this->observers[]=$observer;}}publicfunctionremoveObserver(){if(($index=array_search($observer,$this->observers))!==false){unset($this->observers[$index]);}}公共乐趣动作nitifyObservers(){foreach($this->observersas$observer){$observer->update($this->getPressure(),$this->getTemperature(),$this->getHumidity());}}publicfunctiononChanged(){$this->nitifyObservers();}//获取最新气压publicfunctiongetPressure(){return$this->pressure;}//获取最新温度publicfunctiongetTemperature(){return$this->temperature;}//获取最新的湿度publicfunctiongetHumidity(){return$this->humidity;}//测试公共函数youNeedChanged(){$this->pressure=mt_rand(1,99);$this->temperature=mt_rand(1,99);$this->湿度=mt_rand(1,99);$this->onChanged();}}观察者界面interfaceObserver{//压力/温度/湿度publicfunctionupdate($pressure,$temperature,$humidity);}显示面板界面interfaceDisplayElement{publicfunctiondisplay();}观察者对象集合类CurrentConditionsDisplay实现Observer,DisplayElement{protected$subject;保护$压力,$温度,$湿度;//这里为什么保留Subject接口的引用是为了方便remove和registepublicfunction__construct(Sublect$subject){$this->subject=$subject;$this->subject->registerObserver($this);}publicfunctionupdate($pressure,$temperature,$humidity){$this->pressure=$pressure;$this->temperature=$temperature;$this->湿度=$湿度;$this->显示();}publicfunctiondisplay(){echo"当前压力:{$this->pressure},当前温度:{$this->temperature}";}}//另外两个公告板省略测试$weatherData=newWeatherData();$display=newCurrentConditionsDisplay($w??eatherData);//将当前公告板注册为观察者//$other=newOthersDisplay($w??eatherData);//将当前公告板注册为观察者//$other=newOtherDisplay($weatherData);//将当前公告板注册为观察者$weatherData->youNeedChanged();//气象站数据更新new会导致公告板实时更新//当前压力:33,当前温度:46观察者模式的另一种形式我们知道,观察者总是被动地接受来自主题对象的推送,但是在某些场景下,我们想要观察的观察者可以主动获取数据;毕竟观察者那么多,subjectobject不可能事先知道每个观察者需要的状态,也不会导致即使只需要一点数据也被迫接收一堆数据。我们要重写上面的设计问题。类图基本不变,只是在WeatherData类中增加了setChanged方法,并改变了Observer接口的更新签名。重构后的主题界面interfaceSublect{publicfunctionregisterObserver(Observer$observer);公共功能removeObserver();publicfunctionnitifyObservers($args=null);}interfaceObserver{publicfunctionupdate(Sublect$subject,$object=null);}重构主题对象类WeatherData实现Subect{protected$observers=[];保护$压力,$温度,$湿度,$改变;publicfunctionnitifyObservers($args=null){if($this->changed){foreach($this->observersas$observer){$observer->update($this,$args);$this->changed=false;}}公共函数onChanged(){$this->setChanged();$this->nitifyObservers(['pressure'=>$this->pressure,'temperature'=>$this->temperature,'humidity'=>$this->humidity,]);}publicfunctionsetChanged()//新方法{$this->changed=true;}//其他方法不变}重构公告板对象类CurrentConditionsDisplayimplementsObserver,DisplayElement{protected$subject;受保护的$压力,$温度,$湿度;//这里为什么保留Subject接口的引用是为了方便remove和registerpublicfunction__construct(Sublect$subject){$this->subject=$subject;$this->subject->registerObserver($this);}publicfunctionupdate(Sublect$subject,$object=null){if($subjectinstanceofSublect){//可以拉取最新的数据$this->pressure=$subject->getPressure();$this->temperature=$subject->getTemperature();$this->湿度=$subject->get湿度();//也可以得到$this->pressure=$object['pressure'];$this->temperature=$object['temperature'];$this->humidity=$object['湿度'];$this->display();}publicfunctiondisplay(){echo"当前压力:{$this->pressure},当前温度:{$this->temperature}";}}为什么添加setChanged方法setChanged可以让你在更新观察者时有更多的灵活性,并且可以更恰当地通知观察者。例如,如果没有setChanged方法,当气象站的温度变化十分之一度时,将通知所有观察者。或者,您绝对不想如此频繁地更新它。我们可以在温度变化1度时调用setChanged来执行有效的更新。