Почему декларация метода наследника должна быть совместимой с родителем согласно PHP?

Develar

Новичок
Почему декларация метода наследника должна быть совместимой с родителем согласно PHP?

Класс View из пакета Admin наследует от View из пакета Kernel.
PHP:
class  View_Kernel
{
	public function get($name, $cache = false, $level = null) {}
}

class View_Admin extends View_Kernel
{
	public function get($name, $cache = false) {}
}
Почему PHP выводит ошибку уровня E_STRICT "Declaration of View_Admin::get() should be compatible with that of View_Kernel::get()"? В какой книге/статье можно почитать обоснование этого правила? И как мне решить эту проблему - добавлю $level - будет ругаться Code Analyzer, да и вообще вводить в заблуждение. Иначе проектировать - на мой взгляд это и так оптимальное решение.

PHP 5.1.4.
 

wizard

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

Necromant

Новичок
php 5.1.4 - freeBSD 6.0|7.0 | winXP , выше приведенный код полность рабочий.
 

Develar

Новичок
Necromant
E_ALL | E_STRICT
wizard
В том-то и дело, что читал около 5 книг по ООП и нигде об этом не упоминалось и не говорилось что это плохо. Сейчас вот собираюсь читать Фаулера и дядю Боба. По сути - метод выполняет то же самое действие (реализация клиента не касается) - почему тогда сигнатура метода должна совпадать (с атрибутом открытости все понятно и оправданно).
 

Necromant

Новичок
Просто в даннмо случае более полезно произвести замену параметров, классом, тогда будет соблюдатся единый интерфейс доступа ко всем потомкам. ("Рефакторинг" М. Фаулер)
 

uliss

Новичок
PHP:
function xyz(View_Kernel $obj){
    echo $obj->get(1,2,3);
}

$obj= new View_Admin;
xyx($obj);
Вопрос - куда интерпретатору девать третий параметр (3)?
 

wizard

Новичок
Выкинет на момойку )
Вызван будет метод дочернего класса, если таковой перегружен, иначе метод базового класса.
Что не ясно :eek:

Здесь под перегружен читать "определён", т.к. перегружен может быть метод базового класса =)
 

uliss

Новичок
А выкидывание на помойку переданных параметров не есть хорошо
Ты так не считаешь?

Перегруженные виртуальные функции должны иметь одинаковые списки параметров
 

Necromant

Новичок
ГЫЫЫЫЫЫЫЫЫЫЫЫЫ, еще один ))))))
PHP:
class home {
	public function my($param1=false) {
		echo "my\n";
	} 
}

class home_child extends home {
	public function my($param1=false, $param2=false) {
		echo "child\n";
	}
}

$obj = new home_child();
$obj->my(1);
//call child )))
 

wizard

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

uliss

Новичок
Ребят, вы C++ изучали? Посмотрите как там перегружаются виртуальные функциии
Наследование позволяет работать с объектом через интерфейс родительского класса.
Функции принимающие базовый класс в качестве параметра должны работать и с наследником класса, не зная его конкретного типа.
 

wizard

Новичок
Да, так и есть.
А в чём твой вопрос-то состоит?
Это дело больше касается ООП/ООД, чем языков программирования.
 

uliss

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

whirlwind

TDD infected, paranoid
Читаем в мануале

PHP не поддерживает перегрузку функции, также отсутствует возможность переопределить или удалить объявленную ранее функцию.
Не поддерживает, потому что нет строгого отношения к аргументам. Разницу надо понимать между перегрузкой метода и перегрузкой функции. Перегрузка функции - это то же самое имя, но другой набор аргументов. Перегрузка метода - это переопределение существующего метода базового класса. Но так как перегрузка функций не поддерживается, класс не может содержать несколько функции с одинаковым именем. Если функции с одинаковыми именами PHP создать не позволит, то в наследнике он считает что это тот же самый метод что и в базовом классе. Вот по этому и ругается. Можете попробовать создать два одноименных метода - получите поведение как для двух функций.
 

Develar

Новичок
Necromant
Замена классом. Читал (но не в книге Фаулера), иногда полезно, проблема что метод использовать будут в шаблонах дизайна - там класс не передашь.

uliss
Только PHP :) C удовольствием на настоящих ОО, но специализация уже выбрана :).
Значит дело в "Функции принимающие базовый класс в качестве параметра должны работать и с наследником класса, не зная его конкретного типа". Вот он корень зла подобного решения. А как в других языках - С#, Java - компилятор тоже ругается на это?

Я программирую с использованием языка, а не на языке, поэтому интересует вопрос не как это в PHP, а плохо ли это с точки зрения ООП.
Раз это плохо, значит надо будет перепроектировать. Идей, правда, нет, как же сделать это чисто и без запаха.
 

whirlwind

TDD infected, paranoid
Смотря с какой точки оценивать. Если с точки зрения переносимости и читабельности, то лучше не использовать. Вообще, на самом деле не понятно, по каким причинам список аргументов должен различаться? Например

PHP:
class Runner {
   function move(x,y,speed);
}

class OldRunner extends Runner {
   functiom move(x,y);
}

class YoungRunner extends Runner {
   function move(x,y,speed,wJumps);
}
Какое здесь наследование, если это совершенно разное поведение. Старик может только передвигаться, а молодой прыгать по дороге. Хотя в базовом классе был уговор - требуются координаты и скорость перемещения. Не использование speed в OldRunner - это попытка усечь функциональность базового метода, а добавление wJumps - ее расширить при негласном соглашении о соответствии механизма move. В итоге получаем невозможность работать с OldRunner и YoungRunner как с однотипными абстракциями.

Трудно советовать конкретную реализацию на основе таких абстрактных примеров, но, ИМХО, логичнее было бы сделать так
PHP:
class  View_Kernel 
{ 
    public function getLeveled($level,$name,$cache=false){
        // do something with $level
        // e.g. $this->setActiveLevel()
        return $this->get($name,$cache)
    }

    public function get($name, $cache = false) {} 
} 

class View_Admin extends View_Kernel 
{ 
    public function get($name, $cache = false) {} 
}
В этом случае мы акцентируем внимание на специфичности View_Kernel, то есть демонстрируем экспрессивный подход

либо так

PHP:
class  View_Kernel 
{ 
    public function get($name, $cache = false, $level = null) {} 
} 

class View_Admin extends View_Kernel 
{ 
    public function get($name, $cache = false,$level=null) {} 
}
 

Develar

Новичок
whirlwind
Спасибо. Увлекся наследованием - хотя здесь, кроме названия, ничего общего нет. В очередной раз убедился в полезности высокого уровня сообщения об ошибках и TDD.
 
Сверху