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

时间:2012-05-09 13:12:16

标签: php liskov-substitution-principle

我在升级之前在PHP5.4上测试我现有的代码。我发现以下代码不再有效,因为PHP已经收紧了它的继承模型。由于这种紧缩,我一直在阅读SOLID,特别是Liskov's substitution principle(我是一名自学成才的程序员),以便我可以改进我的代码,而不会受到未来“紧张”的影响。< / p>

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原则?

2 个答案:

答案 0 :(得分:3)

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

展望未来,您的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并在self的签名中使用A::compare()。你确实失去了编译器,迫使你记住定义必要的方法,但这是一个小问题(特别是因为接口只声明了一个方法)。

答案 1 :(得分:0)

我认为您的界面和类需要按如下方式编写才能更加健壮:

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关键字指的是当前类,我建议不要将它用于类型提示参数,因为它不太清楚方法参数是什么。

关于你的equals和Liskov Substitution的实施,这是你触及的一个有趣的主题。我会看到here有关平等和Liskov的有趣讨论。