Я знаю, что не могу перегружать методы в PHP. И, насколько я знаю, private
методы в классе невидимы для классов, которые расширяют базовый класс. Так почему это не работает?
class Base { private function foo($arg) { print "Base $arg"; } } class Child extends Base { public function foo() { print "Child"; } } $c = new Child; print $c->foo();
Ошибка:
PHP Strict Standards: Declaration of Child::foo() should be compatible with Base::foo($arg) in /var/www/boludo.php on line 17
Я предположил, что метод foo($arg)
невидим в классе Child
потому что является private
. Итак, я не перегружаю foo
, я просто создаю метод под названием foo
.
Чтобы исправить Уведомление, просто измените foo()
в Ребенке на
public function foo($arg = null) {
Что касается вопроса «почему это не работает»:
Видимость в PHP строго связана с доступом во время выполнения. Это не влияет на то, как вы можете расширять / составлять / перегружать классы и методы. Ослабление видимости частного метода из супертипа в подтипе добавит отдельный отдельный метод в подтип без доступа к тому же именованному методу в супертипе. Тем не менее, PHP будет предполагать отношения между родителями и дочерними элементами для них. Тем не менее это не вызвало извещение. По крайней мере, не по-своему.
Причина, по которой вы получаете Уведомление, заключается в том, что вы также пытаетесь изменить подпись метода. Ваш foo()
больше не требует передачи $arg
. Когда вы принимаете отношения между родителями и дочерними элементами между этими методами, это проблема, потому что в Принципе замещения Лискова говорится, что «если S является подтипом T, то объекты типа T могут быть заменены объектами типа S» без нарушения программы , Другими словами: если у вас есть код, который использует Base
, вы должны иметь возможность заменить Base
на Child
и программа должна работать, как если бы она использовала Base
.
Предположим, у вашей Base
также есть bar()
общих методов bar()
.
class SomeClientUsingBase { public function doSomethingWithBase(Base $base) { $result = $base->bar(); // …
Теперь представьте, что Child
меняет bar()
чтобы потребовать аргумент. Если вы затем передадите Child
for Base
в клиенте, вы разломете клиент, потому что клиент вызывает $base->bar();
без аргумента.
Очевидно, вы могли бы изменить клиент для передачи аргумента, но тогда код действительно зависит от того, как Child
определил метод, поэтому Typehint ошибочен. На самом деле, Child
не является Base
, потому что он не ведет себя как Base
. Тогда это разбитое наследование.
Теперь самое смешное, если вы удалите этот $arg
из foo()
, вы технически не нарушаете LSP, потому что клиент все равно будет работать. Уведомление здесь неверно. Вызов $base->foo(42)
в клиенте, который ранее использовал Base
, по-прежнему будет работать с Child
потому что Child
может просто игнорировать аргумент. Но PHP хочет, чтобы вы сделали необязательным аргумент.
Обратите внимание, что LSP также применяется к тому, что метод может вернуться. PHP просто не включает тип возврата в подпись, поэтому вы учитываете это сами. Ваши методы должны вернуть то, что вернул Супертип, или что-то, что поведенчески эквивалентно.
Вы можете выполнять перегрузку функций в PHP с помощью функции __call: http://www.php.net/manual/en/language.oop5.overloading.php#object.call
Кроме того, ваша проблема заключается в том, что таким образом вы нарушаете принцип Substitutability: http://en.wikipedia.org/wiki/Liskov_substitution_principle
Что-то, что использует PHP. Таким образом, если вы заменяете объект типа базового класса на один с типом класса Child, нарушается Заменяемость. Вы меняете интерфейс базового класса в производном, удаляя аргумент метода foo (…), и таким образом объекты типа базового класса не могут быть заменены объектами типа класса Child, не нарушая работу программы, нарушая, таким образом, Принцип замещаемости Лискова.