Что такое абстрактный класс php
Перейти к содержимому

Что такое абстрактный класс php

  • автор:

Что такое абстрактный класс php

Абстрактный класс представляет частичную реализацию для классов-наследников.

Абстрактный класс определяется с помощью модификатора abstract , который ставится перед именем класса:

abstract class Messenger

Одной из ключевых особенностей абстрактных классов является то, что мы не можем напрямую создать объекты абстрактного класса с помощью вызова его конструктора:

abstract class Messenger < >$telegram = new Messenger(); // эта строка не будет работать

Абстрактные классы, как и обычные классы, могут определять переменные и константы, методы и конструкторы.

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

abstract class Messenger < abstract function send($message); // абстрактный метод >

При определении абстрактного метода перед словом function ставится модификатор abstract . А после списка параметров метода — точка с запятой.

Абстрактные методы могут размещаться только в абстрактных классах. Обычный неабстрактный класс не может иметь абстрактных методов.

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

Для наследования классом абстрактного класса, как и в общем случае при наследовании, применяется ключевое слово extends . Например:

name = $name; > abstract function send($message); function close() < echo "Выход из мессенджера. "; >> class EmailMessenger extends Messenger < function send($message) < echo "$this->name отправляет сообщение: $message
"; > > $outlook = new EmailMessenger("Outlook"); $outlook->send("Hello PHP 8"); $outlook -> close(); ?> ?>

В данном случае класс EmailMessenger наследуется от абстрактного класса Messenger.

Абстрактный класс определяет абстрактный метод send() , поэтому класс-наследник EmailMessenger должен предоставить реализацию для этого метода.

Так, в данном случае мы получим следующий вывод:

Outlook отправляет сообщение: Hello PHP 8 Выход из мессенджера.

Можно заметить, что абстрактные классы похожи на интерфейсы — и те, и другие могут определять методы без реализации, которые реализуются в других классах. Однако, абстрактные классы, как и обычные классы, могут иметь переменные, неабстрактные методы, конструкторы с реализацией, а интерфейсы нет. Кроме того, в PHP один класс может наследоваться только от одного класса, тогда как один класс может применять сразу несколько интерфейсов.

Абстрактные классы

PHP 5 поддерживает определение абстрактных классов и методов. Класс, который содержит по крайней мере один абстрактный метод, должен быть определен как абстрактный. Следует помнить, что нельзя создать экземпляр абстрактного класса. Методы, объявленные абстрактными, несут, по существу, лишь описательный смысл и не могут включать реализации.

При наследовании от абстрактного класса, все методы, помеченные абстрактными в родительском классе, должны быть определены в классе-потомке; кроме того, область видимости этих методов должна совпадать (или быть менее строгой). Например, если абстрактный метод объявлен как protected, то реализация этого метода должна быть либо protected либо public, но никак не private. Более того, сигнатуры методов должны совпадать, т.е. контроль типов (type hint) и количество обязательных аргументов должно быть одинаковым. К примеру, если в дочернем классе указан необязательный параметр, которого нет в сигнатуре абстрактного класса, то в данном случае конфликта сигнатур не будет. Это правило также применяется к конструкторам начиная с версии PHP 5.4, ранее сигнатуры конструкторов могли отличаться.

Пример #1 Пример абстрактного класса

abstract class AbstractClass
/* Данный метод должен быть определён в дочернем классе */
abstract protected function getValue ();
abstract protected function prefixValue ( $prefix );

/* Общий метод */
public function printOut () print $this -> getValue () . «\n» ;
>
>

class ConcreteClass1 extends AbstractClass
protected function getValue () return «ConcreteClass1» ;
>

public function prefixValue ( $prefix ) return » < $prefix >ConcreteClass1″ ;
>
>

class ConcreteClass2 extends AbstractClass
public function getValue () return «ConcreteClass2» ;
>

public function prefixValue ( $prefix ) return » < $prefix >ConcreteClass2″ ;
>
>

$class1 = new ConcreteClass1 ;
$class1 -> printOut ();
echo $class1 -> prefixValue ( ‘FOO_’ ) . «\n» ;

$class2 = new ConcreteClass2 ;
$class2 -> printOut ();
echo $class2 -> prefixValue ( ‘FOO_’ ) . «\n» ;
?>

Результат выполнения данного примера:

ConcreteClass1 FOO_ConcreteClass1 ConcreteClass2 FOO_ConcreteClass2

Пример #2 Пример абстрактного класса

abstract class AbstractClass
// Наш абстрактный метод должен определять только необходимые аргументы
abstract protected function prefixName ( $name );

class ConcreteClass extends AbstractClass

// Наш дочерний класс может также определять необязательные аргументы, не указанные в сигнатуре родительского метода
public function prefixName ( $name , $separator = «.» ) if ( $name == «Pacman» ) $prefix = «Mr» ;
> elseif ( $name == «Pacwoman» ) $prefix = «Mrs» ;
> else $prefix = «» ;
>
return » < $prefix > < $separator > < $name >» ;
>
>

$class = new ConcreteClass ;
echo $class -> prefixName ( «Pacman» ), «\n» ;
echo $class -> prefixName ( «Pacwoman» ), «\n» ;
?>

Результат выполнения данного примера:

Mr. Pacman Mrs. Pacwoman

Код, предназначенный для прежних версий PHP, должен работать без изменений, если в нём отсутствуют классы или функции, именованные ‘abstract’.

Абстрактные классы в ООП на PHP

Пусть у вас есть класс User , а от него наследуют классы Employee и Student .

При этом предполагается, что вы будете создавать объекты классов Employee и Student , но объекты класса User — не будете, так как этот класс используется только для группировки общих свойств и методов своих наследников.

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

Для этого существуют так называемые классы. Абстрактные классы представляют собой классы, предназначенные для наследования от них. При этом объекты таких классов нельзя создать.

Для того, чтобы объявить класс абстрактным, нужно при его объявлении написать ключевое слово abstract :

Итак, давайте напишем реализацию абстрактного класса User . Пусть у него будет приватное свойство name , а также геттеры и сеттеры для него:

name; > public function setName($name) < $this->name = $name; > > ?>

Попытка создать объект класса User вызовет ошибку:

А вот унаследовать от нашего класса будет можно. Сделаем класс Employee , который будет наследовать от нашего абстрактного класса User :

salary; > public function setSalary($salary) < $this->salary = $salary; > > ?>

Создадим объект класса Employee — все будет работать:

setName(‘john’); // метод родителя, т.е. класса User $employee->setSalary(1000); // свой метод, т.е. класса Employee echo $employee->getName(); // выведет ‘john’ echo $employee->getSalary(); // выведет 1000 ?>

Аналогично можно создать объект класса Student , наследующий от User :

scholarship; > public function setScholarship($scholarship) < $this->scholarship = $scholarship; > > ?>

Самостоятельно, не подсматривая в мой код, реализуйте такой же абстрактный класс User , а также классы Employee и Student , наследующие от него.

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

Абстрактные классы также могут содержать абстрактные методы. Такие методы не должны иметь реализации, а нужны для того, чтобы указать, что такие методы должны быть у потомков. А собственно реализация таких методов — уже задача потомков.

Для того, чтобы объявить метод абстрактным, при его объявлении следует написать ключевое слово abstract .

Давайте попробуем на практике. Пусть предполагается, что все потомки класса User должны иметь метод increaseRevenue ( увеличить доход ).

Этот метод должен брать текущий доход пользователя и увеличивать его на некоторую величину, переданную параметром.

Сам класс User не знает, какой именно доход будет получать наследник — ведь у работника это зарплата, а у студента — стипендия. Поэтому каждый потомок будет реализовывать этот метод по-своему.

Фишка тут в том, что абстрактный метод класса User заставляет программиста реализовывать этот метод в потомках, иначе PHP выдаст ошибку. Таким образом вы, или другой программист, работающий с вашим кодом, никак не сможете забыть реализовать нужный метод в потомке.

Итак, давайте попробуем на практике. Добавим абстрактный метод increaseRevenue в класс User :

name; > public function setName($name) < $this->name = $name; > // Абстрактный метод без тела: abstract public function increaseRevenue($value); > ?>

Пусть наш класс Employee пока останется без изменений. В этом случае, даже если не создавать объект класса Employee , а просто запустить код, в котором определяются наши классы, — PHP выдаст ошибку.

Давайте теперь напишем реализацию метода increaseRevenue в классе Employee :

salary; > public function setSalary($salary) < $this->salary = $salary; > // Напишем реализацию метода: public function increaseRevenue($value) < $this->salary = $this->salary + $value; > > ?>

Проверим работу нашего класса:

setName(‘john’); // установим имя $employee->setSalary(1000); // установим зарплату $employee->increaseRevenue(100); // увеличим зарплату echo $employee->getSalary(); // выведет 1100 ?>

Реализуем метод increaseRevenue и в классе Student . Только теперь наш метод будет увеличивать уже стипендию:

scholarship; > public function setScholarship($scholarship) < $this->scholarship = $scholarship; > // Метод увеличивает стипендию: public function increaseRevenue($value) < $this->scholarship = $this->scholarship + $value; > > ?>

Добавьте в ваш класс User такой же абстрактный метод increaseRevenue . Напишите реализацию этого метода в классах Employee и Student .

Добавьте также в ваш класс User абстрактный метод decreaseRevenue ( уменьшить зарплату ). Напишите реализацию этого метода в классах Employee и Student .

Некоторые замечания

При наследовании от абстрактного класса, все методы, помеченные абстрактными в родительском классе, должны быть определены в дочернем классе.

При этом область видимости этих методов должна совпадать или быть менее строгой. Что значит менее строгой: например, если абстрактный метод объявлен как protected , то реализация этого метода должна быть protected или public , но не private .

Объявления методов также должны совпадать: количество обязательных параметром должно быть одинаковым. Однако класс-потомок может добавлять необязательные параметры, которые не были указаны при объявлении метода в родителе.

Практика

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

Давайте сделаем для этого абстрактный класс Figure с двумя абстрактными методами getSquare и getPerimeter .

Почему класс Figure абстрактный: потому что он не описывает реально существующую геометрическую фигуру и, соответственно, объект этого класса мы не будем создавать.

Почему методы getSquare и getPerimeter абстрактные: потому что каждая фигура имеет свой алгоритм вычисления площади и периметра и, соответственно, класс Figure не может написать реализацию этих методов.

Зачем нам вообще нужен класс Figure : чтобы наследовать от него и таким образом заставить всех наследников реализовать указанные методы.

Итак, напишем реализацию класса Figure :

Пусть теперь мы хотим создать класс Quadrate для описания геометрической фигуры квадрат. Как известно, у квадрата все стороны равны, поэтому для описания квадрата нам нужно задать только его ширину.

Давайте для этого сделаем приватное свойство $a , значение которого будет задаваться в конструкторе класса:

Давайте теперь унаследуем наш класс Quadrate от класса Figure :

a = $a; > > /* Код класса не рабочий и будет выдавать ошибку, так как мы не написали реализацию методов родителя. */ ?>

Сейчас наша реализация класса Quadrate не рабочая, так как мы не написали реализацию абстрактных методов родителя.

Давайте сделаем это:

a = $a; > public function getSquare() < return $this->a * $this->a; > public function getPerimeter() < return 4 * $this->a; > > ?>

Давайте создадим квадрат со стороной 2 и найдем его площадь и периметр:

getSquare(); // выведет 4 echo $quadrate->getPerimeter(); // выведет 8 ?>

Сделайте аналогичный класс Rectangle ( прямоугольник ), у которого будет два приватных свойства: $a для ширины и $b для длины. Данный класс также должен наследовать от класса Figure и реализовывать его методы.

Усложним

Сейчас все методы класса Figure — абстрактные. Это, конечно же, не обязательно. Пусть наш класс имеет еще и метод getRatio , который будет находить отношение площади к периметру (то есть одно делить на второе).

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

Почему мы можем написать реализацию этого метода прямо в классе Figure : потому что этот метод будет одинаковым для всех потомков.

Итак, добавим наш метод:

getSquare() / $this->getPerimeter(); > > ?>

Обратите внимание на следующее: хотя методы getSquare и getPerimeter абстрактные и не имеют реализации, мы их все равно можем использовать в своем методе getRatio , хотя реализация этих методов появится только в потомках.

Применим наш метод:

getSquare(); // выведет 4 echo $quadrate->getPerimeter(); // выведет 8 echo $quadrate->getRatio(); // выведет 0.5 ?>

Добавьте в класс Figure метод getSquarePerimeterSum , который будет находить сумму площади и периметра.

Абстрактный класс PHP

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

Абстрактные методы не содержат никакого определения их реализации. Проще говоря, вы не должны писать код для метода, это просто объявление. Если в классе есть один метод, являющийся абстрактным, класс должен быть объявлен, как абстрактный. Абстрактный класс PHP ООП также может содержать абстрактные методы.

Когда класс наследуется от абстрактного класса, необходимо, чтобы все методы, объявленные как абстрактные в родительском классе, были определены в производном классе с тем же модификатором доступа. Если метод определен в абстрактном суперклассе как protected , то в производном классе он должен быть protected или public , но не private . Методы должны быть одинаковыми по именам и аргументам.

Пример абстрактного класса:

 > class Bus extends Vehicle < public function getnoofWheel() < return 4; >> class Bicycle extends Vehicle < public function getnoofWheel() < return 2; >> function printNumWheels(Vehicle $v) < echo "A " . $v->getName() . " has " . $v->getnoofWheels() . " wheelsn"; > $Bus1 = new Bus(); $Bicycle1 = new Bicycle(); printNumWheels($Bus1); printNumWheels($Bicycle1); /***Результат приведенного выше кода будет следующим***/ A Bus has 4 wheels A Bicycle has 2 wheels

В приведенном выше ООП PHP примере мы сначала объявили абстрактный класс Vehicle , он содержит один абстрактный метод getnoofWheel . Этот метод является лишь объявлением и не содержит никакого кода. Этот класс также содержит обычный ( неабстрактный ) метод getName , который возвращает имя данного класса. После этого мы объявили класс Bus , который наследуется от класса Vehicle .

В классе Bus мы определили функцию getnoofWheel . Точно так же расширяется другой класс Bicycle , и в нем определяется абстрактный метод. Из классов Vehicle и Bicycle создаются объекты, и мы используем функцию для вывода количества колес транспортных средств. Этот пример наглядно иллюстрирует использование абстрактного класса в PHP 5 ООП и его реализацию.

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *