ООП. Наследование классов в 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. Осталась только одна очень важная вещь - это полиморфизм. Но о ней я вскоре расскажу в другой статье. Надеюсь, я доступно объяснил то, что сам недавно изучил
Вы забыли удалить слово abstract при корпировании кода (там, где “Т.е. если бы мы написали так…”)
Нет, я там ничего не забыл. Смысл написанного “Т.е. если бы мы написали так…” был в том, чтобы показать, что абстрактный метод не имеет тела. Там мной приведено два примера: первый - без тела, второй - с телом. Просто этого не заметно с первого взгляда.
сп, статья порадовала, лаконична:).
ага, объяснили все доступно, спасибо, но самое главное лаконично, после прочтения последней строчки, мог не успел взорваться ))
Статья хорошо написана. молодец
Скажите, из-за чего может дочерний класс не наследовать переменные?
у меня именно так и происходит, хотя даже ошибок нет и не выдает:(
думаю из-за того что они объявлены как private-переменные)) я новичок)) так что прошу сильно не ругать))
Классная статья, еще можно добавить про статичные методы и св-ва
Спасибо, статья очень помогла!
А можно совсем глупый вопрос задать? Можно? Спасибо, щас задам.
Нахрена вообще в PHP интерфейсы?
Я понимаю, для чего нужны интерфейсы, например, в Java. Когда два класса MyClass1 и MyClass2 реализуют один и тот же интерфейс Inter1, то их обоих можно будет передавать в функцию, которая принимает параметр типа Inter1. Поскольку в Java строгая типизация, то это важно: если передать функции что-то не то, то компилятор заругается.
Но в PHP-то интерфейсы тогда зачем? Тут же нет строгой проверки по значению. Неважно, реализуют MyClass1 и MyClass2 интерфейс Inter1, или просто каждый из них независимо содержит функцию fn1() - всё равно с ними можно будет без проблем работать, держа в уме что они “как бы реализуют Inter1″. Так какой тогда смысл в наследовании? Чисто идеологически, что ли, для собственного понимания?
На мой взгляд, в PHP эти интерфейсы никому не нужны. А вот множетственное наследование не помешало бы…
Сейчас изучаю ООП в PHP для своего нового проекта, наткнулся на эту статью, спасибо, многое понял. Вот только мне, как и комментатору до меня, совсем не понятно, на кой чёрт нужно объявление интерфейсов, если там всё равно нет рабочего кода?
С абстрактными классами ещё можно придумать применение (хотя тоже не очень понятно), а вот с интерфейсами? Странно.
[…] вам понять принцип наследования в PHP. Иначе выражаясь, наследования классов в PHP выгодно для каждого […]
Интерфейсы видимо нужны только для наглядности кода, при разработке несколькими программистами.
Грубо говоря интерфейс - это директивный комментарий
мне казалось что строчка (во втором из примеров)
$this->mark = 5; // Ошибка доступа!
не должна вызывать никакой ошибки просто создаст своё свойство “новое” mark в созданном объекте.
проверил - скопировал пример классов с наследованием, создал объект класса GraduateStudent, вызвал метод someFn(), но никакой ошибки.
Что-то с чем-то не так?
Вот таких бы статеек побольше. В общем порадовала меня, всё на пальцах! Только один фиг, не доперло до меня зачем нужны абстрактные классы (и методы)! Как он может на примере применяться… хоть кусочек кода ^^
Хорошая статья…..
А как быть с тем что некоторые классы написанные с богатым использованием private, а в них нужно изменить одну функцию
как сделать их копию? Если сделать
class CopyMyClass extends MyClass
punblic my_f() {
/*новый код */
}
То my_f не сработает, т.к. куча переменных стали не доступны….
Очень помогло
День добрый. Комментарий к “Абстрактные классы и методы”.
Цитата: “А зачем тогда нужны абстрактные методы и классы? А чтобы описать объект, который будет реализован, но который еще не реализован. А здесь я приведу пример, как использовать абстрактный метод базового класса в производном классе:” Не понял я зачем нужны абстрактные методы и классы. Ответ не понятен, смысл тогда описывать то чего пока что нету? По сути в абстрактном классе мы создаём лишь названия переменных и методов, которые не будут иметь значений. Не могу понять зачем.
Ваш пример:
fn(); // Выводит “привет”
?>
Дурацкий пример, как по мне. Если убрать MyClass и его наследование для класса MyClass1 то всё будет работать с тем же успехом. Не вижу смысла.
Спасибо.
День добрый. Комментарий к “Абстрактные классы и методы”.
Цитата: “А зачем тогда нужны абстрактные методы и классы? А чтобы описать объект, который будет реализован, но который еще не реализован. А здесь я приведу пример, как использовать абстрактный метод базового класса в производном классе:” Не понял я зачем нужны абстрактные методы и классы. Ответ не понятен, смысл тогда описывать то чего пока что нету? По сути в абстрактном классе мы создаём лишь названия переменных и методов, которые не будут иметь значений. Не могу понять зачем.
Ваш пример:
fn(); // Выводит “привет”
?>
Дурацкий пример, как по мне. Если убрать MyClass и его наследование для класса MyClass1 то всё будет работать с тем же успехом. Не вижу смысла.
Спасибо.
Хорошая статья, все понятно. Молодец.