Варианты обращения к методам родительского класса

Ulen

Новичок
Решил начать изучение ООП, т.к писанина функций уже осточертела и появился вопрос:

Имеем 2 класса, второй класс наследует первый:

PHP:
class getUserInfo {
   
    public $uid;

    public function __construct($uid) {
        $this -> uid = $uid;
    }

    function get_info_DB() {
        $db = new PDO('sqlite:info_data.db');
        $uid = $this -> uid;
        $value = $db -> query('SELECT * FROM `user_info` WHERE `user_id` = '.$uid.'');
        $result = $value -> fetch(PDO::FETCH_ASSOC); // array

        return $result;
    }
}

class showUserInfo extends getUserInfo {
   
    public $user_id;
    public $div_class_name;
   
    public function __construct($user_id, $div_class_name) {
        $this -> user_id = $user_id;
        $this -> div_class_name = $div_class_name;
    }
   
    function show_info() {
       
        $user_id = $this -> user_id;
        $div_class_name = $this -> div_class_name;
       
        $this -> uid = $user_id;
        $user_info = $this -> get_info_DB();
       
        echo ('<div class="'.$div_class_name.'"><span class="text_bold">'.$user_info['name'].'</span></div>');
    }
}
Во втором классе мы обращаемся к методу из первого:
PHP:
$user_info = $this -> get_info_DB();
Однако к нему можно обратиться и таким образом:
PHP:
$user_info = getUserInfo::get_info_DB();
Есть ли разница между этими вариантами? Читал мануалы, в некоторых пишут первый вариант, в некоторых второй.
 

Вурдалак

Продвинутый новичок
Если закрыть глаза на качество приведённого кода, то всегда нужно писать $this->foo() для обращения к методу родительского класса.

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

fixxxer

К.О.
Партнер клуба
Очень просто.
1) За исключением п.2, всегда надо писать $this->foo()
2) При вызове родительского метода непосредственно из перегруженного метода (то есть когда class B extends A, в B переопределяется метод foo(), и из этого переопределенного foo надо вызвать родительскую реализацию) - писать parent::foo().

Обращаться по явному имени не надо вообще никогда, это наследие php4 (или вообще php3? не помню уже), в котором не было ключевого слова parent. В современном php запись foo::bar() существует исключительно для вызова статических методов.
 

Ulen

Новичок
Спасибо, понял.

Вурдалак, а чем плох код? Я подозреваю, что из-за конструкторов?
Я не возмущаюсь, критика это хорошо - можно исправить ошибки и не допускать их в будущем.
 

Вурдалак

Продвинутый новичок
А, я че-то про parent:: и не сказал :) Но вообще http://martinfowler.com/bliki/CallSuper.html

Обращаться по явному имени не надо вообще никогда, это наследие php4 (или вообще php3? не помню уже), в котором не было ключевого слова parent. В современном php запись foo::bar() существует исключительно для вызова статических методов.
Технически, есть отличие, ты можешь написать Foo extends Bar extends Baz и при вызове Baz::method() внутри класса Foo метод Bar::method() вызываться не будет. Но это уже серьезные архитектурные проблемы. :)
 

whirlwind

TDD infected, paranoid
Когда начинаешь разбираться с ООП, очень важно понять, что означает обращение к методу экземпляра
Код:
$user_info = $this -> get_info_DB();
Это пример полиморфизма. В случае с наследованием - это внутренний полиморфизм типа. Это почти то же самое, что и обычный полиморфизм, внешний по отношению к потребителю (пользователю) класса, только распространяется по иерархии наследования. Ну и, как следствие, распространяется на защищенные члены класса. Приведенная строчка, независимо от того где она была вызвана, всегда приведет к вызову последней реализации метода в иерархии наследования. Отсюда получаем множество полезных эффектов, которые можно свести к определению полиморфизма: при сохранении внешнего вида можно спокойно подменять реализацию. И все будет работать как задумано в начальном алгоритме. В случае наследования - так, как задумано в базовом классе. Например, в базовом классе какая нибудь формула, которая через методы класса получает значения для вычислений по этой формуле. В наследниках можно переопределить эти методы, что бы они возвращали другие значения. Формула останется прежней, а результат изменится. При этом сам алгоритм рассчета по формуле будет 100% надежен, так как изменения его не коснулись. Принцип возможности подмены при сохранении внешнего вида (интерфеса) наиболее важная часть в ООП. После осознания того, как это работает внутри иерархии наследования можно переходить к более продвинутым топикам: декомпозиции и внедрения зависимостей.
 

fixxxer

К.О.
Партнер клуба
А, я че-то про parent:: и не сказал :) Но вообще http://martinfowler.com/bliki/CallSuper.html
Ну... не всегда (скажем, в конструкторе - вполне ок), но вообще да.

Технически, есть отличие, ты можешь написать Foo extends Bar extends Baz и при вызове Baz::method() внутри класса Foo метод Bar::method() вызываться не будет. Но это уже серьезные архитектурные проблемы. :)
Это очень серьезные архитектурные проблемы :)
 

С.

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

fixxxer

К.О.
Партнер клуба
Так себе правило. Щас он подпишет к именам классов слово Helper, и по этому правилу все станет хорошо. :)
 

С.

Продвинутый новичок
Helper уже не так плохо на фоне getUserInfo и showUserInfo.
 

fixxxer

К.О.
Партнер клуба
Да такая же фигня - процедуры, зачем-то обернутые в класс. Немногим лучше. Впрочем, *Helper - это всегда плохо.
 

AnrDaemon

Продвинутый новичок
OP, у меня вопрос - "взять инфу пользователя" - это какая часть речи?
 
Сверху