我试图理解观察者模式。到目前为止一切都很顺利,我知道事情是如何运作的。
但是我有一个问题。在Head First: Design Patterns书中,有一个气象站程序的例子。
-
WeatherData
类为Subject
-
CurrentConditionDisplay
和StatisticsDisplay
为观察者
我已经实现了所有需要的代码。但是我总是得到这个错误:
CurrentConditionDisplay::update()的声明必须兼容ObserverInterface::更新()
现在我知道导致这个错误的原因是接口中的update()
方法与在参数方面实现它的具体类不同。
但在CurrentConditionDisplay
类我有update($temp, $humidity)
在接口我有update()
。我的意思是如何在一个类中指定update()
方法需要两个参数(温度和湿度),而在另一个类中需要(温度和压力)?
是否有任何解决方案,而不是在ObserverInterface
做update(SubjectInterface $subject)
?
下面是我的代码:
接口:
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;
}
}
这只是对你发布的代码的补充:你正在实现观察者/主题模式,不知怎么错了。
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
}
}
然后,在Observer中,你应该接收到Subject
public function update( Subjectinterface $subject )
{
if ( "bar" === $this->foo() )
// do something if the Subject has the desired state
}
意味着两件事:
- 你的受试者不需要知道观察者的数量和类型。它只知道它得到了一些,当它以某种方式改变它的状态时,它需要通知它们。
- 每个观察者都需要决定它是否应该运行并对状态变化做出反应。
总的来说,我建议阅读更多关于这个模式的细节。
那么,你在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 )