Принцип подстановки Лискова и интерфейсы PHP - PullRequest
1 голос
/ 15 июня 2019

Является ли следующий код прямым нарушением принципа подстановки Лискова:
Дочерние классы никогда не должны нарушать определения типов родительского класса.

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 Ответ

2 голосов
/ 16 июня 2019

Тот факт, что ваш пример незаконен, не нарушает 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 все еще может принимать объекты классов Foo и Baz, , поскольку объекты подтипа могут использоваться вместо объектов подтипа , точно так же, какЛСП говорит.

То, что вы не можете сделать, это написать метод с несовместимой подписью, и будущая поддержка ковариации и контравариантности также не позволит этого.


ОтносительноПоддержка ковариации и контравариантности в типах возвращаемых данных и типах параметров : в PHP 7.4 есть поддержка, поступающая в продажу в конце 2019 года.

Вы можете прочитать о деталях (принято) предложение здесь .

  • Ковариация будет поддерживаться только для возвращаемых типов (поэтому определения теперь могут указывать подклассы T, если родительский класс или интерфейс объявилитип возвращаемого значения T)

  • Контравариантность будет поддерживаться для типов параметров (так что подклассы или реализующие классы теперь могут объявлять супертипы T в качестве типа параметра, если родитель илиинтерфейс объявил тип параметра T)

При определении совместимости метода с его родительским движкомТеперь следует разрешить менее конкретные типы параметров и более конкретные возвращаемые типы, если новые типы по-прежнему принимают типы, указанные родителями.Другими словами: тип параметра может быть заменен одним из его супертипов, а возвращаемый тип может заменять подтип.

Это, опять же, позволит поддержку LSP и позволит пользователям классачтобы иметь возможность доверять абстракциям, против которых они программируют, вместо того, чтобы проверять специфику конкретного класса, который они используют.

...