Liskov替代原理和PHP接口

时间:2019-06-15 10:55:15

标签: php oop liskov-substitution-principle

以下代码是否直接违反了Liskov替换原则:
子类永远不能破坏父类的类型定义。

class Baz {}
class Foo extends Baz {}

interface a
{
    public function baz(Baz $baz);
}

class b implements a
{
    public function baz(Foo $foo)
    {
    }
}

以下结果:

  

致命错误:b :: baz(Foo $ foo)的声明必须与a :: baz(Baz $ baz)兼容

1 个答案:

答案 0 :(得分:2)

您的示例是非法的事实并不会破坏LSP。您的问题是正在定义一个接口,并且期望在实现该接口时遵守该合同。

通过实现接口a,然后尝试使方法签名不兼容,类b的用户可以尝试调用b::baz()并由于a::baz()的签名而失败期望一个Baz,并且您在b::baz()上的不兼容实现期望一个实例Foo

例如,如果您提出的建议是合法的,则可能会发生:

$baz = new Baz();

$b = new b();

// since a::baz(Baz) is specified, the class user believes this
// should be possible, but your illegal implementation
// breaks that expectation

$b->baz($baz); // Not accepted!

这根本不会破坏LSP。

正确实现了您的接口a的类仍可以接受类FooBaz的对象,因为是子类型的对象完全可以使用LSP来代替超级类型

您不能做的是编写一个具有不兼容签名的方法,将来的协方差和协方差支持也将不允许这样做。


关于返回类型和参数类型的协方差和协方差支持:PHP 7.4即将在2019年底发布该支持。

您可以了解(接受的)提案here的详细信息。

  • 仅对返回类型支持协方差(因此,如果父类或接口声明了返回类型为T,则定义现在可以指定T的子类)

    < / li>
  • 参数类型将支持一致性(因此,如果父类或接口声明的参数类型为T,则子类或实现类现在可以将T的超类型声明为参数类型。 )

  

在确定方法与其父方法的兼容性时,只要新类型仍然接受父方法指定的类型,引擎现在应允许使用较少特定的参数类型和更特定的返回类型。换句话说:参数类型可以代替其父类型之一,而返回类型可以代替子类型。

再次,这将允许LSP支持,并使类用户能够信任他们针对其进行编程的抽象,而不必检查其正在使用的具体类的细节。