在升级之前,我正在 PHP5.4 上测试我的现有代码。我发现下面的代码不再有效,因为 PHP 已经收紧了它的继承模型。由于这种紧缩,我一直在阅读有关 SOLID 的信息,特别是 Liskov 的替换原则(我是一个自学成才的程序员(,这样我就可以改进我的代码,而不会遭受未来的"收紧"。
interface IComparable {
public function equals(self $other);
}
class A implements IComparable{
protected $var;
public function __construct($v){
$this->var=$v;
}
public function equals(self $other){
return ($this->var == $other->var) ? 'equal' : 'different';
}
}
$a1= new A(7);
$a2= new A(5);
$a3= new A(5);
echo $a1->equals($a2),"'n";
echo $a2->equals($a3),"'n";
PHP 5.3 结果:
- 不同
- 平等
PHP 5.4 结果:
PHP 致命错误:A::equals(( 的声明必须与 IComparable::equals(IComparable $other(
如果我这样编写代码,我可以避免 php5.4 错误:
interface IComparable {
public function equals($other);
}
class A implements IComparable{
protected $var;
public function __construct($v){
$this->var=$v;
}
public function equals($other){
if(get_class($other) != get_class($this)) return false;
return ($this->var == $other->var) ? 'equal' : 'different';
}
}
但是修复是否符合 Liskov 的替换原则,因为该函数显然不会接受任何参数类型?如果没有,我如何编写一个可继承的函数来执行我需要的操作 - 比较相同类型的 2 个对象 - 并符合良好的 OOD 原则?
首先:PHP 5.3 行为是一个错误,所以你不能用它作为判断任何其他方法的标准。
展望未来,您的 5.3 版本代码中已经违反了 LSP。考虑:
interface IComparable {
public function equals(self $other);
}
这说明"任何IComparable
都可以将自己与任何其他IComparable
进行比较"(比较的语义对于讨论并不重要(。然后,class A
继续违反 LSP,因为它不支持与任何IComparable
进行比较 - 仅与碰巧是A
实例的那些进行比较。
PHP 5.4 版本没有违反 LSP,因为新版本的 IComparable
说"我可以将自己与任何其他对象进行比较",这正是class A
所做的。
如果您打算保留 5.3 版本合同,那么IComparable
应该阅读
interface IComparable {
public function equals(IComparable $other);
}
class A
当然会使用相同的签名。这不会违反 LSP,并且可以在两个版本中正常工作。
如果你的意图是声明"一个IComparable
实例可以将自身与相同类型的实例进行比较,不管是什么",那么你就不走运了,因为这个协定不能使用方法签名和类型提示来表达。
更新:原来你的意图是声明"这个类的实例可以比较自己",IComparable
只是为了迫使你不要忘记这样做。
在这种情况下,解决方案就是简单地忘记IComparable
并在A::compare()
的签名中使用self
。您确实失去了编译器强制您记住定义必要方法的问题,但恕我直言,这是一个小问题(特别是因为接口只声明一种方法(。
我相信你的界面和类需要写成如下才能更健壮:
interface IComparable
{
/**
* @return boolean
*/
public function equals(IComparable $other);
/**
* @return mixed
*/
public function getValue();
}
class A implements IComparable{
protected $var;
public function __construct($v){
$this->var=$v;
}
/**
* @return mixed
*/
public function getValue()
{
return $this->var;
}
/**
* @param IComparable $other
* @return boolean
*/
public function equals(IComparable $other)
{
return ( $this->getValue() === $other->getValue() );
}
}
php 中的 self 关键字是指当前类,我建议不要将其用于类型提示参数,因为它不太清楚方法参数是什么。
关于你实现平等和李斯科夫替换,这是一个你提到的有趣的主题。我会在这里看到关于平等和利斯科夫的有趣讨论。