我正在玩一些设计模式,并希望使用SPL的观察者模式创建一个示例。因为让观察者和主题完全通用是没有意义的,所以我想扩展接口以使它们更加特定于手头的应用程序。问题是,当我运行下面的代码时,我得到的错误如“DataAccess :: update()必须与SplObserver :: update()的代码兼容”。
我知道通过切换方法签名以匹配接口的签名,我可以使代码无错误地执行。我的问题是:为什么不允许签名中定义的类的子项?下面,ModelObserver是SplObserver,Model是SplSubject。我原以为这会起作用。我错过了什么吗?
仅供参考,我知道我可以使用界面中定义的显式方法签名,并在我的代码逻辑中使用instanceof关键字来实现相同的功能。我只是希望找到一个更优雅的解决方案。谢谢!
<?php
interface ModelObserver extends SplObserver {
}
class DataAccess implements ModelObserver {
/*
* (non-PHPdoc) @see SplObserver::update()
*/
public function update(Model $subject) {
// TODO Auto-generated method stub
}
}
// Just a generic model for the example
class Model implements SplSubject {
private $_properties = array ();
private $_observers = array ();
/*
* generically handle properties you wouldn't want to do it quite like this
* for a real world scenario
*/
public function __get($name) {
return $this->_properties [$name];
}
public function __set($name, $value) {
$this->_properties [$name] = $value;
}
public function __call($method, $args) {
if (strpos ( $method, 'get' ) === 0) {
$name = lcfirst ( str_replace ( 'get', '', $method ) );
return $this->_properties [$name];
}
if (strpos ( $method, 'set' ) === 0) {
$name = lcfirst ( str_replace ( 'set', '', $method ) );
$this->_properties [$name] = $args [0];
return $this;
}
}
public function __toString() {
return print_r ( $this, true );
}
/*
* (non-PHPdoc) @see SplSubject::attach()
*/
public function attach(ModelObserver $observer) {
$this->_observers [] = $observer;
return $this;
}
/*
* (non-PHPdoc) @see SplSubject::detach()
*/
public function detach(ModelObserver $observer) {
if (in_array ( $observer, $this->_observers )) {
$f = function ($value) {
if ($value != $observer) {
return $value;
}
};
$observers = array_map ( $f, $this->_observers );
}
return $this;
}
/*
* (non-PHPdoc) @see SplSubject::notify()
*/
public function notify() {
foreach ($this->_observers as $observer) {
$observer->update($this);
}
}
}
$da = new DataAccess();
$model = new Model ();
$model->setName ( 'Joshua Kaiser' )->setAge ( 32 )->setOccupation ( 'Software Engineer' )
->attach($da);
echo $model;
答案 0 :(得分:3)
限制DataAccess::update()
接受您的孩子Model
违反此界面的合同。
是的,所有Model
个对象都属于SplSubject
个类,但并非所有SplSubject
属于类Model
。接口是一种合同,保证实现类支持接口支持的所有内容。
您的代码如果有效,则会将DataAccess::update()
方法仅限制为Model
子类,而不是更广泛的父类SplSubjects
。您不能缩小传递给接口定义的方法的参数范围。
假设您在Model类中添加了属性public $foo
。如果允许,您可以在DataAccess::update()
方法中使用该属性$foo
。有人可以将SplSubjects
扩展到没有OddModel
属性的子$foo
。他们无法再将OddModel
传递到您的DataAccess::update()
函数中 - 如果它们可能会因为$foo
没有OddModel
属性而中断。
这是接口背后的整个想法,通过实现它们,您同意100%支持接口定义的内容。在这种情况下,您的界面会说:
如果您实施我,则必须接受
的每个{{1}}或类SplSubject
扩展SplSubject
您实施的界面试图破坏合同。