代码是否符合Liskov的替代原则?

dna*_*irl 5 php liskov-substitution-principle

我在升级之前在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";
Run Code Online (Sandbox Code Playgroud)

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';
    }
}
Run Code Online (Sandbox Code Playgroud)

修正是否符合Liskov的替换原则,因为该函数显然不接受任何参数类型?如果没有,我怎么能编写一个可以执行我需要的可继承功能 - 比较相同类型的2个对象 - 并遵守良好的OOD原则?

Jon*_*Jon 3

首先:PHP 5.3的行为是一个bug,所以你不能用它作为判断任何其他方法的标准。

展望未来,您的 5.3 版本代码中已经违反了 LSP。考虑:

interface IComparable {
    public function equals(self $other);
}
Run Code Online (Sandbox Code Playgroud)

这表示“任何人都IComparable可以将自己与任何其他人进行比较IComparable”(比较的语义对于讨论并不重要)。thenclass A继续违反 LSP,因为它不支持与任何实例进行比较IComparable——仅与那些恰好是A实例的实例进行比较。

PHP 5.4 版本并没有违反 LSP,因为新版本IComparable说“我可以将自己与任何其他对象进行比较”,这也正是这样做class A的。

如果您的意图是保留 5.3 版本合同,那么IComparable应该阅读

interface IComparable {
    public function equals(IComparable $other);
}
Run Code Online (Sandbox Code Playgroud)

当然class A会使用相同的签名。这不会违反 LSP ,并且可以在两个版本中正常工作。

如果您的意图是声明“IComparable实例可以将自身与相同类型的实例进行比较,无论是什么”,那么您就不走运了,因为该契约无法使用方法签名和类型提示来表达。

更新:事实证明,您的意图是声明“此类的实例可以相互比较”,IComparable其目的只是为了迫使您不要忘记这样做。

在这种情况下,解决方案就是简单地忘记并在 的签名中IComparable使用。您确实会失去编译器的强迫您记住定义必要方法的能力,但恕我直言,这是一个小问题(特别是因为接口仅声明一个方法)。selfA::compare()