«Перегрузка» частного метода в PHP

Я знаю, что не могу перегружать методы в 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, не нарушая работу программы, нарушая, таким образом, Принцип замещаемости Лискова.