我将使用以下示例来说明我的问题:
class Attribute {}
class SimpleAttribute extends Attribute {}
abstract class AbstractFactory {
abstract public function update(Attribute $attr, $data);
}
class SimpleFactory extends AbstractFactory {
public function update(SimpleAttribute $attr, $data);
}
如果你尝试运行它,PHP 将抛出一个致命错误,指出Declaration of SimpleFactory::update() must be compatible with that of AbstractFactory::update()
我完全明白这意味着什么:SimpleFactory::update()
方法签名必须与其父抽象类的方法签名完全匹配。
但是,我的问题:有没有办法允许具体方法(在这种情况下,SimpleFactory::update()
)将类型提示重新定义为原始提示的有效后代?
一个例子是instanceof
运算符,在以下情况下将返回 true:
SimpleAttribute instanceof Attribute // => true
我确实意识到,作为一种解决方法,我可以在具体方法中使类型提示相同,并在方法主体本身中进行 instanceof 检查,但是有没有一种方法可以简单地在签名级别强制执行此操作?
我不希望如此,因为它可能会破坏类型提示合同。假设一个函数foo
采用 AbstractFactory 并传递给 SimpleFactory。
function foo(AbstractFactory $maker) {
$attr = new Attribute();
$maker->update($attr, 42);
}
...
$packager=new SimpleFactory();
foo($packager);
foo
calls update
并将一个属性传递给工厂,它应该采用该属性,因为AbstractFactory::update
方法签名承诺它可以采用属性。嘭! SimpleFactory 有一个无法正确处理的类型的对象。
class Attribute {}
class SimpleAttribute extends Attribute {
public function spin() {...}
}
class SimpleFactory extends AbstractFactory {
public function update(SimpleAttribute $attr, $data) {
$attr->spin(); // This will fail when called from foo()
}
}
在契约术语中,后代类必须尊重其祖先的契约,这意味着函数参数可以变得更基础/更少指定/提供更弱的契约,并且返回值可以更派生/更指定/提供更强的契约。 Eiffel(可以说是最流行的合同设计语言)的原理在“Eiffel 教程:继承和契约 http://docs.eiffel.com/book/method/et-inheritance#Inheritance_and_contracts”。类型的弱化和强化就是例子逆变和协变 http://en.wikipedia.org/wiki/Covariance_and_contravariance_%28computer_science%29, 分别。
从更理论的角度来说,这是 LSP 违规的一个例子。不,不that LSP http://adventuretime.wikia.com/wiki/Lumpy_Space_Princess; the 里氏替换原则 http://en.wikipedia.org/wiki/Liskov_substitution_principle,它指出子类型的对象可以替换超类型的对象。SimpleFactory
是一个子类型AbstractFactory
, and foo
需要一个AbstractFactory
。因此,根据 LSP 的说法,foo
应该采取SimpleFactory
。这样做会导致“调用未定义的方法”致命错误,这意味着 LSP 已被违反。
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)