我试图理解观察者模式。到目前为止,它很顺利,我理解事情是如何运作的。
但我有一个问题。在 Head First:Design Patterns 一书中,有一个气象站计划的例子。
WeatherData
类是Subject
CurrentConditionDisplay
和StatisticsDisplay
是观察员我已经实现了所需的所有代码。但我总是得到这个错误:
CurrentConditionDisplay :: update()声明必须兼容 使用ObserverInterface :: update()
现在我知道导致此错误的原因是接口中的update()
方法与根据参数实现它的具体类不同。
但在CurrentConditionDisplay
课程中,我在update($temp, $humidity)
接口中有update()
。我的意思是如何指定一个类中的update()
方法采用两个参数(温度和湿度)而另一个类采用(温度和压力)?
是否有任何解决方案,而不是update(SubjectInterface $subject)
中的ObserverInterface
?
这是我的代码:
接口
interface SubjectInterface
{
public function registerSubscriber(ObserverInterface $observerInterface);
public function removeSubscriber(ObserverInterface $observerInterface);
public function notifySubscriber();
}
interface ObserverInterface
{
public function update();
}
interface DisplayInterface
{
public function display();
}
**课程:**
WeatherData
class WeatherData implements SubjectInterface
{
/**
* @var array
*/
protected $observers = [];
protected $temp;
protected $humidity;
protected $pressure;
public function registerSubscriber(ObserverInterface $observer)
{
$this->observers[] = $observer;
}
public function removeSubscriber(ObserverInterface $observer)
{
$i = array_search($this->observers, $observer);
if ($i) {
unset($this->observers[$i]);
}
}
public function notifySubscriber()
{
foreach ($this->observers as $observer) {
/** @var $observer ObserverInterface */
$observer->update();
}
}
public function setValues($temp, $humidity, $pressure)
{
$this->temp = $temp;
$this->humidity = $humidity;
$this->pressure = $pressure;
$this->notifySubscriber();
}
}
CurrentConditionDisplay
class CurrentConditionDisplay implements ObserverInterface, DisplayInterface
{
protected $temp;
protected $humidity;
public function __construct(SubjectInterface $subject) {
$subject->registerSubscriber($this);
}
public function display()
{
if(isset($this->temp) && isset($this->humidity)) {
echo sprintf("CURRENT:<br>Temp: %d, Humidity: %d <br>", $this->temp, $this->humidity);
}
}
public function update($new_temp, $new_humidity)
{
$this->temp = $new_temp;
$this->humidity = $new_humidity;
$this->display();
}
}
StatisticDisplay
class StatisticDisplay implements ObserverInterface, DisplayInterface
{
protected $temp;
protected $pressure;
public function __construct(SubjectInterface $subject) {
$subject->registerSubscriber($this);
}
public function display()
{
if(isset($this->temp) && isset($this->pressure)) {
return sprintf("STATISTIC:<br>Ration: %d <br>", $this->temp / $this->pressure);
}
}
public function update($new_temp, $new_pressure)
{
$this->temp = $new_temp;
$this->humidity = $new_pressure;
}
}
答案 0 :(得分:1)
这只是您发布的代码的补充:您正在以某种方式错误地实现Observer / Subject模式。
Subject::register()
方法(WeatherData::registerSubscriber()
)应该用于实际向对象添加观察者。这应该发生在某种控制器中 - 而不是在观察者本身。问自己:观察者如何自我注册主题?这赢了工作:
public function __construct( SubjectInterface $subject ) {
$subject->registerSubscriber($this);
}
相反,它应该是将自己传递给观察者的主体。换一种说法: 您的实际主题(由观察者监控)通知每个观察者。
// This class `implements SubjectInterface` */
public function notifySubscriber()
{
foreach ($this->observers as $observer) {
/** @var $observer ObserverInterface */
$observer->update( $this ); // HERE WE HAND OVER THE SUBJECT TO THE OBSERVER
}
}
然后,在你的观察者中,你应该收到主题
public function update( Subjectinterface $subject )
{
if ( "bar" === $this->foo() )
// do something if the Subject has the desired state
}
这意味着两件事:
总的来说,我建议reading a bit more in detail关于这种模式。
答案 1 :(得分:0)
嗯,CurrentConditionDisplay::update()
中的方法就是这个
public function update($new_temp, $new_humidity)
当你implement
ObserverInterface::update()
方法时应该是这样的:
public function update();
当PHP告诉你
时兼容
CurrentConditionDisplay::update()
声明必须与ObserverInterface::update()
然后它告诉你你的方法必须匹配 - 名称,可访问性(public|protected|private
)和参数。
因此,如果您希望其实现类具有参数,则您的接口需要具有不同的方法:
public function update( $new_temp, $new_humidity )