ООП. Наследование классов в php

Классы в php версии 5 могут наследоваться, т.е. приобретать свойства и методы своего родителя. А зачем вообще нужно наследование классов?

Рассмотрим наследование на примере человека. Я - человек. И я наследую некоторые свойства класса Человек, например возможность говорить, интеллект, необходимость в воздухе, воде, пище и всяких витаминах. Эти свойства не уникальны для каждого отдельного человека, т.е. всем людям присуще то, что я перечислил. Мы еще можем заметить, что класс Человек наследует зависимость от воды, воздуха и пищи у класса Млекопитающие. А этот класс наследует эти свойства у класса Животные.

Благодаря наследованию мы можем очень много сэкономить на описании реального объекта. Например, спросите меня - что такое утка? Я отвечу: это птица, которая крякает. Я вроде бы сказал всего 4 слова, но полностью в этих четырех словах уместилась вся необходимая информация для описания утки, потому что сказав «птица, которая крякает», я дал вам знать, что утке присущи все свойства птицы плюс свойство «кряканье» :)

Наследуем

Короче тем самым объектно-ориентированные языки позволяют создавать модели, очень близкие к реальному миру. Для примера студентов и аспирантов мы можем сказать, что аспирант - это не простой студент. Он обладает дополнительными свойствами, но одновременно он обладает и всеми свойствами студента. Поэтому мы можем унаследовать класс GraduateStudent (аспирант) от класса Student (студент) с помощью ключевого слова extends:

<?
    class Student {
    }
    class GraduateStudent extends Student {
    }
?>

Тут класс Student будет называться базовым, а класс GraduateStudent - производным.

Наследование еще позволяет нам избавиться от повторения кода, потому что все открытые (public, protected) методы и свойства класса Student становятся доступными и в классе GraduateStudent. Помните, в статье ранее я говорил, что модификаторы доступа методов/свойств private и protected отличаются? Вот. Если у нас есть свойство (или метод) private в классе Student, то оно не будет доступно в классе GraduateStudent:

<?
    class Student {
        private   $mark;
        protected $average;
    }

    class GraduateStudent extends Student {

        function someFn() {
            $this->mark = 5;      // Ошибка доступа!
            $this->average = 4.4; // А так правильно
        }

    }
?>

Кстати. Помните, что если свойство или метод в классе не имеют модификатора доступа, то по умолчанию они public? Видимо это было введено для совместимости с предыдущими версиями пхп, где модификаторов еще не было, а все члены класса были публичными.

Вспомним еще конструкторы и деструкторы. Так вот, если нам надо вызвать конструктор или деструктор базового класса, то надо это делать явно, через указатель parent:

<?
    class MyClass {
        function __construct() {
            echo "Запущен конструктор базового класса";
        }
        function __destruct() {
            echo "Запущен деструктор базового класса";
        }
    }

    class MyClass1 extends MyClass {
        function __construct() {
            parent::__construct();
        }
        function __destruct() {
            parent::__destruct();
        }
    }  

    $obj = new MyClass1(); // Выводит "Запущен конструктор базового класса"
    unset($obj);           // Выводит "Запущен деструктор базового класса"
?>

Сейчас это может показаться лишним, но в реальной практике это зачастую очень нужно. Я знаю это по собственному опыту написания программ на C#.

Абстрактные классы и методы

Это тоже новинка в PHP5. Абстрактные методы имеют только объявление и не имеют реализации. Класс, который содержит такие методы, должен быть обязательно объявлен как абстрактный:

<?
    abstract class MyClass {
        abstract public function fn();
    }
?>

Т.е. если бы мы написали так…

<?
    abstract class MyClass {
        abstract public function fn() {
        }
    }
?>

…интерпретатор пхп заругался бы на нас.

Абстрактные классы могут еще содержать и обычные (не абстрактные) элементы. Создать объект абстрактного класса мы не имеем права. Можно только определять новые классы от базового абстрактного класса и создавать объекты уже от производных классов.

А зачем тогда нужны абстрактные методы и классы? А чтобы описать объект, который будет реализован, но который еще не реализован.

А здесь я приведу пример, как использовать абстрактный метод базового класса в производном классе:

<?
    abstract class MyClass {
        abstract public function fn();
    }

    class MyClass1 extends MyClass {
        public function fn() {
            echo “привет”;
        }
    }

    $obj = new MyClass1;
    $obj->fn(); // Выводит “привет”
?>

Интерфейсы

В моем понимании интерфейс - нечто, с помощью чего мы можем управлять объектом. Например, возьмем телевизор. У него есть интерфейс - панель с кнопками спереди под экраном или справа (слева) от экрана. Ну пульт управления - это тоже интерфейс. Т.е. большинство из нас не знает, как сделан телевизор и как он работает, но мы знаем, как им можно управлять (переключать каналы, настраивать громкость, яркость и т.д.). То же самое было придумано и в программировании.

С точки зрения программиста, интерфейс (interface) - это абстрактный класс, который содержит только абстрактные методы и не имеет никаких свойств.

Основное отличие интерфейсов от абстрактных классов - в том, что в PHP 5 класс не может быть порожден от нескольких классов (и абстрактных в т.ч.), но зато может быть создан на основе любого числа интерфейсов. При этом в интерфейсе методы должны объявляться ключевым словом function без указания всяких спецификаторов (в т.ч. и abstract):

<?
    interface Inter1 {
        function fn1();
    }

    interface Inter2 {
        function fn2();
    }

    class MyClass implements Inter1, Inter2 {
        public function fn1() {
            echo 1;
        }
        public function fn2() {
            echo 2;
        }
    }
    $obj = new MyClass;
    $obj->fn1(); // Выведет 1
    $obj->fn2(); // Выведет 2
?>

Также в ООП есть такое понятие, как множественное наследование. Суть его в том, что один и тот же класс может иметь несколько базовых классов. Но для PHP5 это не совсем справедливо. В PHP5 класс не может быть унаследован от нескольких базовых классов, но зато может реализовать сколько угодно интерфейсов, как показано в последнем примере.

Финальные методы и классы

Интересная возможность в пхп 5, на которую я наткнулся в ходе изучения ООП, - это возможность определять финальные методы и классы.

Метод, который мы определили с ключевым словом final, в дальнейшем мы не можем переопределить в классах, которые производны от нашего класса:

<?
    class MyClass {
        final public function fn() {
            // Код метода
        }
    }
    class MyClass1 extends MyClass {
        // Следующий код вызывает ошибку
        // переопределения финального метода
        // базового класса MyClass
        public function fn() {
            // Код метода
        }
    }
?>

И еще: если мы используем final при определении самого класса, то не сможем больше породить от него другие классы:

<?
    final class MyClass {
        // Код описания класса
    }

    // Следующий код вызывает ошибку
    // порождения от финального класса
    class MyClass1 extends MyClass {
        // Код описания класса
    }
?>

Тут заметим, что если класс определен как final, то и все методы этого класса автоматически станут финальными, и определять их явно как final уже не надо.

А как насчет свойств? Свойства класса определять финальными нельзя. Ну в этом не было бы смысла, думаю. Потому что, в отличие от методов, мы не можем переопределять свойства класса. Мы можем их только наследовать.

Выводы

На этом я почти заканчиваю рассмотрение объектно-ориентированного программирования в PHP5. Осталась только одна очень важная вещь - это полиморфизм. Но о ней я вскоре расскажу в другой статье. Надеюсь, я доступно объяснил то, что сам недавно изучил :)





Читайте также:



20 Ответов на “ООП. Наследование классов в php”

  1. Вы забыли удалить слово abstract при корпировании кода (там, где “Т.е. если бы мы написали так…”) :)

  2. novice

    Нет, я там ничего не забыл. Смысл написанного “Т.е. если бы мы написали так…” был в том, чтобы показать, что абстрактный метод не имеет тела. Там мной приведено два примера: первый - без тела, второй - с телом. Просто этого не заметно с первого взгляда.

  3. slayersilence

    сп, статья порадовала, лаконична:).

  4. Jenia

    ага, объяснили все доступно, спасибо, но самое главное лаконично, после прочтения последней строчки, мог не успел взорваться ))

  5. Статья хорошо написана. молодец

  6. Скажите, из-за чего может дочерний класс не наследовать переменные?
    у меня именно так и происходит, хотя даже ошибок нет и не выдает:(

  7. grinder

    думаю из-за того что они объявлены как private-переменные)) я новичок)) так что прошу сильно не ругать))

  8. tylik

    Классная статья, еще можно добавить про статичные методы и св-ва

  9. ochenhoroshiyparenvasyadaushiykomment

    Спасибо, статья очень помогла!

  10. А можно совсем глупый вопрос задать? Можно? Спасибо, щас задам.

    Нахрена вообще в PHP интерфейсы?

    Я понимаю, для чего нужны интерфейсы, например, в Java. Когда два класса MyClass1 и MyClass2 реализуют один и тот же интерфейс Inter1, то их обоих можно будет передавать в функцию, которая принимает параметр типа Inter1. Поскольку в Java строгая типизация, то это важно: если передать функции что-то не то, то компилятор заругается.

    Но в PHP-то интерфейсы тогда зачем? Тут же нет строгой проверки по значению. Неважно, реализуют MyClass1 и MyClass2 интерфейс Inter1, или просто каждый из них независимо содержит функцию fn1() - всё равно с ними можно будет без проблем работать, держа в уме что они “как бы реализуют Inter1″. Так какой тогда смысл в наследовании? Чисто идеологически, что ли, для собственного понимания?

    На мой взгляд, в PHP эти интерфейсы никому не нужны. А вот множетственное наследование не помешало бы… :-(

  11. Сейчас изучаю ООП в PHP для своего нового проекта, наткнулся на эту статью, спасибо, многое понял. Вот только мне, как и комментатору до меня, совсем не понятно, на кой чёрт нужно объявление интерфейсов, если там всё равно нет рабочего кода?

    С абстрактными классами ещё можно придумать применение (хотя тоже не очень понятно), а вот с интерфейсами? Странно.

  12. […] вам понять принцип наследования в PHP. Иначе выражаясь, наследования классов в PHP выгодно для каждого […]

  13. Tankha

    Интерфейсы видимо нужны только для наглядности кода, при разработке несколькими программистами.
    Грубо говоря интерфейс - это директивный комментарий :)

  14. мнительный

    мне казалось что строчка (во втором из примеров)
    $this->mark = 5; // Ошибка доступа!
    не должна вызывать никакой ошибки просто создаст своё свойство “новое” mark в созданном объекте.

    проверил - скопировал пример классов с наследованием, создал объект класса GraduateStudent, вызвал метод someFn(), но никакой ошибки.
    Что-то с чем-то не так?

  15. Павел Третьяков

    Вот таких бы статеек побольше. В общем порадовала меня, всё на пальцах! Только один фиг, не доперло до меня зачем нужны абстрактные классы (и методы)! Как он может на примере применяться… хоть кусочек кода ^^

  16. Z

    Хорошая статья…..

    А как быть с тем что некоторые классы написанные с богатым использованием private, а в них нужно изменить одну функцию

    как сделать их копию? Если сделать
    class CopyMyClass extends MyClass
    punblic my_f() {
    /*новый код */
    }
    То my_f не сработает, т.к. куча переменных стали не доступны….

  17. piter

    Очень помогло

  18. День добрый. Комментарий к “Абстрактные классы и методы”.

    Цитата: “А зачем тогда нужны абстрактные методы и классы? А чтобы описать объект, который будет реализован, но который еще не реализован. А здесь я приведу пример, как использовать абстрактный метод базового класса в производном классе:” Не понял я зачем нужны абстрактные методы и классы. Ответ не понятен, смысл тогда описывать то чего пока что нету? По сути в абстрактном классе мы создаём лишь названия переменных и методов, которые не будут иметь значений. Не могу понять зачем.

    Ваш пример:
    fn(); // Выводит “привет”
    ?>

    Дурацкий пример, как по мне. Если убрать MyClass и его наследование для класса MyClass1 то всё будет работать с тем же успехом. Не вижу смысла.

    Спасибо.

  19. День добрый. Комментарий к “Абстрактные классы и методы”.

    Цитата: “А зачем тогда нужны абстрактные методы и классы? А чтобы описать объект, который будет реализован, но который еще не реализован. А здесь я приведу пример, как использовать абстрактный метод базового класса в производном классе:” Не понял я зачем нужны абстрактные методы и классы. Ответ не понятен, смысл тогда описывать то чего пока что нету? По сути в абстрактном классе мы создаём лишь названия переменных и методов, которые не будут иметь значений. Не могу понять зачем.

    Ваш пример:
    fn(); // Выводит “привет”
    ?>

    Дурацкий пример, как по мне. Если убрать MyClass и его наследование для класса MyClass1 то всё будет работать с тем же успехом. Не вижу смысла.

    Спасибо.

  20. Aidar

    Хорошая статья, все понятно. Молодец.


© Copyright. . I-Novice. All Rights Reserved. Terms | Site Map