Подтвердить что ты не робот

Почему PHP допускает "несовместимые" конструкторы?

Вот несколько фрагментов:

  • Метод переопределения конструктора имеет дополнительный параметр.

    class Cat {
        function __construct() {}
    }
    
    class Lion extends Cat {
        function __construct($param) {}
    }
    
  • Метод переопределения (регулярный) имеет дополнительный параметр.

    class Cat {
        function doSomething() {}
    }
    
    class Lion extends Cat {
        function doSomething($param) {}
    }
    

Первое будет работать, а второе - Declaration of Lion::doSomething() should be compatible with that of Cat::doSomething().

Почему особое отношение к конструкторским методам?

4b9b3361

Ответ 1

Чтобы понять, почему к ним относятся по-разному, вы должны понимать Принцип замены Лискова, который состояния

Если для каждого объекта o1 типа S существует объект o2 типа T такой, что для всех программ P, определенных в терминах T, поведение P не изменяется, когда o1 заменяется на o2, тогда S является подтипом T "- БарбараЛисков, абстракция данных и иерархия, заметки SIGPLAN, 23,5 (май 1988 г.).

В двух словах это означает, что любой класс, использующий ваши Lion или Cat, должен надежно называть doSomething на нем, независимо от того, какой класс тот или другой. Если вы измените подпись метода, это больше не будет гарантировано (вы можете расширить его, но не сузить его).

Очень простой пример

public function doSomethingWithFeline(Cat $feline)
{
    $feline->doSomething(42);
}

Так как Lion extends Cat, вы установили отношение is-a, то есть doSomethingWithFeline примет Lion для Cat. Теперь представьте, что вы добавили требуемый аргумент в doSomething в Lion. Вышеприведенный код сломается, потому что он не передает этот новый параметр. Следовательно, необходимость в совместимых сигнатурах.

LSP не применяется к конструкторам, но поскольку подтипы могут иметь разные зависимости. Например, если у вас есть FileLogger и DBLogger, для ctors (конструкторов) первого потребуется имя файла, в то время как для последнего потребуется адаптер db. Таким образом, ctors - это конкретные реализации, а не часть контракта между классами.

Ответ 2

Принцип замены Лискова утверждает, что "если S является подтипом T, то объекты типа T могут быть заменены объектами типа S". В вашем примере это означает, что вы должны иметь возможность заменять объекты типа Cat объектами типа Lion.

Вот почему ваш второй код не разрешен. Вы больше не сможете выполнять эту подстановку, так как вы больше не сможете вызвать метод ->doSomething() без аргументов.

Конструктор, с другой стороны, не подчиняется Принципу подстановки Лискова, поскольку он не является частью результирующего API объекта. Вы все равно сможете подставить результирующие объекты, независимо от соответствия подписей конструктора.

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

Ответ 3

__construct(), поскольку они уникальны для каждого класса. Конструктор для Lion не является тем же самым конструктором для Cat, хотя если Lion extends Cat и Lion не имеет __construct(), вы все равно можете расширить родительский __construct() с помощью Lion::__construct().

В отличие от других методов PHP не будет генерировать уровень E_STRICT сообщение об ошибке, когда __construct() переопределяется с помощью разных параметры, чем у родительского метода __construct().

Руководство по PHP: конструкторы и деструкторы

Другие магические методы принимают конкретные аргументы, что означает, что их аргументы и т.д. всегда будут согласованы.

После того, как классы создаются, тогда полиморфизм/переопределение для вашего doSomething(). Ваш родительский класс, например, как абстрактный класс, определяет аргументы и видимость, которые должны соответствовать дочернему классу для поддержки переопределения.

Ответ 4

Конструктор предназначен только для конкретного объекта. Это рождает его.

Другие методы - до тех пор, пока этот подтип связан с унаследованным классом, поэтому эти методы являются частью одного и того же объекта и поэтому должны быть совместимы при распространении по определению нескольких классов.

В противном случае вы создадите объект, который немного шизо.


Изменить: Если вы также хотите ввести строгие проверки с помощью сигнатуры конструктора, вы можете использовать interface с PHP 5.2, который добавляет конструктор:

Добавлена ​​поддержка конструкторов в интерфейсах для проверки синтаксиса конструктора в реализациях. Начиная с PHP 5.2.0, интерфейсы могут иметь конструкторы. Однако, если вы решите объявить конструктор в интерфейсе, каждый класс, реализующий этот интерфейс, ДОЛЖЕН включать конструктор с подписями, сопоставляемый с конструктором базового интерфейса. Под "подписями" мы понимаем определения типа и типа возвращаемого типа, включая подсказки любого типа, и включаем, передаются ли данные по ссылке или по значению.

(возьмите из Другие улучшения - переход с PHP 5.1.x на PHP 5.2.x)

Ответ 5

То, что вы описываете, overloading, которое не поддерживается PHP. Теперь, когда вы создаете конструктор для класса, он используется только в этом классе, а родительский constuctor по умолчанию не вызывается (см. Конструкторы и деструкторы Вам нужно называть его вручную parent::__construct().

Когда вы создаете метод в своем классе, он напрямую обращается к родительскому методу.

Итак, в заключение:

  • constuctor не использует перегрузку, а только для самого класса
  • метод использует перегрузку и поэтому не допускается