5

在升级之前,我正在 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 原则?

4

2 回答 2

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只是为了强迫你不要忘记这样做。

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

于 2012-05-09T13:31:30.843 回答
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 的实现,这是你触及的一个有趣的主题。我会在这里看到关于平等和 Liskov 的有趣讨论。

于 2012-05-09T13:41:07.263 回答