Лекция 8 ООП в PHP . Объектно-ориентированное программирование (ООП) неразрывно связано с понятиями Класса и Объекта. Класс является описываемой на языке терминологии исходного кода моделью ещё не существующей сущности (объекта). Фактически он описывает устройство объекта, являясь своего рода чертежом. Говорят, что объект — это экземпляр класса. Обычно классы разрабатывают таким образом, чтобы их объекты соответствовали объектам предметной области. Пример класса в PHP: class Объект - Сущность в адресном пространстве вычислительной системы, появляющаяся при создании экземпляра класса или копирования прототипа. class MyClass{ public $property; // Свойство или “поле” класса // Метод public function SomeMethod(){ // Код метода } } 1. Инкапсуляция, полиморфизм и наследование. ООП позволяет программисту моделировать в создаваемых приложениях реальные объекты и ситуации. Технология ООП обладает тремя главными преимуществами: она проста для понимания: ООП позволяет мыслить категориями повседневных объектов; повышенно надежна и проста для сопровождения — правильное проектирование обеспечивает простоту расширения и модификации объектно-ориентированных программ. Зачастую модульная структура позволяет вносить независимые изменения в разные части программы, сводя к минимуму риск ошибок программирования; ускоряет цикл разработки — модульность и здесь играет важную роль, поскольку различные компоненты объектно-ориентированных программ можно легко использовать в других программах, что уменьшает избыточность кода и снижает риск внесения ошибок при копировании. Специфика ООП заметно повышает эффективность труда программистов и позволяет им создавать более мощные, масштабируемые и эффективные приложения. Объектно-ориентированное программирование основано на 3 основных принципах : Инкапсуляции, наследовании и полиморфизме. Теперь поясним на примерах вышеупомянутые принципы. 1.1. Инкапсуляция. Инкапсуляция — это свойство системы, позволяющее объединить данные и методы, работающие с ними, в классе, и скрыть детали реализации от пользователя. Предположим, у нас имеется некий класс Vehicle, моделирующий устройство и поведение некой простейшей технической единицы, скажем, в компьютерной игре. class Vehicle{ // Запас “жизней” юнита public $hits; // Метод, отвечающий за получение юнитом повреждений в игре... public function getDamage($damage){ $this->hits -= $damage; } } В этом простом примере в классе, моделирующем объект, мы описали некие данные - запас “жизней” игрового юнита - $hits, а также запрограммировали реализацию обработки этих данных - метод, получения юнитом повреждений getDamage(). Таким образом, говорят, что данные и механизмы их обработки/модификации инкапсулированы в классе Vehicle. 1.2. Наследование. Наследование — это свойство системы, позволяющее описать новый класс на основе уже существующего с частично или полностью заимствующейся функциональностью. Класс, от которого производится наследование, называется базовым, родительским или суперклассом. Новый класс — потомком, наследником, дочерним или производным классом. Продолжим наш пример с описанием объекторов некоторой компьютерной игры. Предположим у нас есть также класс объектов Tank, который логически также принадлежит к технике. Вместо того, чтобы описывать Tank как полностью отдельный и самомтоятельный класс с дублированием всех тех же данных и методов, что есть у любой техники (Vehicle) в игре, можно описать класс Tank, как дочерний класс от класса Vehicle, дополнив его только свойствами и методами, которые свойственны именно классу Tank: ... class Tank extends Vehicle { // Броня public $armor; // Окопаться public function trench(){ // Реализация... } } Таким образом, класс Tank полностью наследует разрешенный* набор полей и методов родительского класса Vehicle вместе с реализацией, без необходимости определять их в самом классе Tank т.к. их реализация вполне подходит и для данного класса. Но на практике часто возникает потребность изменить поведение родительского класса в классах-потомках, и тут в игру вступает вид следующего принципа - полиморфизма наследования. 1.3. Полиморфизм. Полиморфизм (одно из определений) - (многоформенность) является следствием идеи наследования. В общих словах, полиморфность класса — это свойство базового класса использовать функции производных классов, даже если на момент определения еще неизвестно, какой именно класс будет включать его в качестве базового и, тем самым, становиться от него производным. Рассмотрим следующий вид полиморфизма: Предположим, каждый юнит в нашей игре также имеет название, которое хранится в поле $name; class Vehicle{ // Название public $name; … // Метод, возвращающий строку с названием юнита. public function getIntroductionPhrase(){ return “I’m abstract unit ” . $this->name; } // Представить юнить: public function introduce(){ echo $this->getIntroductionPhrase(); } } Базовый класс дополним 2мя методами. Один из которых возвращает строку, представляющую данный юнит. Второй - просто выводит эту строку в код страницы. Далее переопределим метод getIntroductionPhrase() в классе Tank, который наследует данный класс. class Tank extends Vehicle { ... // Метод, возвращающий строку с названием юнита. public function getIntroductionPhrase(){ return “I’m Tank ” . $this->name . “. Awaiting for your orders.”; } } Таким образом, если мы создадим экземпляр класса Tank и вызовем унаследованный метод introduce(), то в нём в итоге будет вызван не унаследованный метод getIntroductionPhrase(), а тот, который определён именно в классе Tank: ... $tank1 = new Tank(); $tank1->name = “T-82”; $tank1->introduce(); … Вывод: I’m Tank T-82. Awaiting for your orders. 2. Директивы области видимости полей и методов. Как вы уже успели заметить, в нашем примере часто фигурируют модификаторы public (protected, private). Эти директивы определяют область видимости и влияют на наследуемость полей и методов. Рассмотрим разницу между ними: К public (общедоступным) свойствам и методам, можно получить доступ из любого контекста. К protected (защищенным) свойствам и методам можно получить доступ либо из содержащего их класса, либо из его подкласса. Никакому внешнему коду доступ к ним не предоставляется. Вы можете сделать данные класса недоступными для вызывающей программы с помощью ключевого слова private (закрытые). К таким свойствам и методам можно получить доступ только из того класса, в котором они объявлены. Даже подклассы данного класса не имеют доступа к таким данным. Т.е. программист при проектировании должен определить, какие данные или методы класса могут являться общедоступными, а какие являются сугубо приватными и следует ограничить доступ к ним. 3. Статические свойства и методы. Очень важной особенностью ООП является наличие статических свойств и методов. Такие свойства и методы принадлежат не объекту, а классу в целом. То есть, существуют данные, которые являются индивидуальными для каждого экземпляра объекта, например: имя, кол-во “жизней” и т.д. Но на ряду с ними существуют и данные, которые также относятся к классу, но являются общими для всех объектов этого класса. Примером статических полей может быть счётчик созданных объектов этого класса. class Tank extends Vehicle { … public static $count_of_instances = 0; // Метод, возвращающий строку с названием юнита. public function __construct(){ $this->name = ‘Tank_’ . (++self::$count_of_instances); } // Возвращает кол-во объектов класса public static function getCount(){ return self::$count_of_instances; } } Таким образом, мы организовали также и автоматическую генерацию имён юнитов на основе счётчика объектов класса. И каждый новый объект будет иметь уникальное имя. Сам счётчик является общим для всех объектов этого класса. Метод getCount() также в данном случае является статическим и может быть вызван вне контекста конкретного объекта, а напрямую через имя класса: … echo Tank::getCount(); … 4. Абстрактные классы. В наших предидущих примерах мы использовали некий базовый класс Vehicle, для наследования. При этом класс этот отражает лишь абстрактное понятие техники, как общего класса объектов. Если бы мы попытались представить некий объект класса техника (Vehicle), то не смогли бы описать нечто конкретное, ни его внешний вид ни конкретное поведение. То есть класс Vehicle описывает некое обстрактное понятие и класс этот не предназначен для воплощения в объект, а служит лишь прототипом для наследования. Поэтому вполне логично было бы объявить наш класс Vehicle как абстрактный. Абстрактные классы в PHP не могут иметь реализации, т.е. нельзя создать экземпляр класса, который объявлен как абстрактный. abstract class Vehicle{ ... } 5. Интерфейсы. Класс может быть пронаследован только от одного класса, соответственно в PHP 4 объект мог принадлежать только к одному семейству типов. В отличии от него PHP 5 поддерживает интерфейсы: классоподобные структуры, позволяющие поместить объект сразу в несколько семейств типов, гарантируя тем самым, что объект поддерживает более одного набора методов. Интерфейсы объявляются при помощи ключевого слова interface. В принципе они похожи на абстрактные классы, но в отличии от них вообще не должны содержать в себе реализации методов. Интерфейс содержит в себе только атрибуты и абстрактные методы. Если некий класс наследует некий интерфейс, то интерфейс (или набор интерфейсов) какбы обязывает класс иметь реализацию для указанных в интерфейсе методов. Например, мы имеем несколько подвидов техники: Некоторые юниты могут двигаться, некоторые - атаковать другие юниты, некоторые - и то и другое. Предположим у нас есть 2 соответствующих интерфейса: interface MovableUnit { // Класс обязан реализовать метод move function move(); } interface BattleUnit { // Класс обязан реализовать метод attack function attack(); } Теперь можем создавать необходимые классы, которые поддерживают и реализуют эти интерфейсы: class Tank extends Vehicle implements MovableUnit, BattleUnit{ // Может передвигаться и атаковать } class Turret extends Vehicle implements BattleUnit{ // Может только атаковать } class RepairUnit extends Vehicle implements MovableUnit{ // Может передвигаться } Каждый класс может поддерживать неограниченное кол-во интерфейсов, но не может иметь более одного родителя.