Объектно-ориентированное программирование для начинающих


Для многих PHP программистов объектно-ориентированное программирование является пугающей концепцией, забитой сложным синтаксисом и другими препятствиями на пути к освоению. В данной статье концепция объектно-ориентированного программирования (ООП) представляется в виде стиля кодирования, который позволяет группировать взаимосвязанные действия в классы, чтобы создать более компактный и эффективный код. Что такое объектно ориентированное программирование Объектно-ориентированное программирование — это стиль кодирования, который позволяет разработчику группировать схожие задачи в классы. Таким образом код соответствует принципу DRY (don't repeat yourself – не повторяй самого себя) и становится лёгким для сопровождения. Одним из преимуществ DRY программирования является то, что если некоторая информация требует изменения вашей программы, то нужно изменять код лишь в одном месте, чтобы обновить алгоритм. Одним из самых страшных ночных кошмаров разработчика является сопровождение кода, в котором данные объявляются снова и снова, что превращает любые изменения в программе в бесконечную игру «прятки», в ходе которой приходится охотиться на повторяющиеся данные и куски алгоритма. ООП пугает многих разработчиков, потому что оно вводит новый синтаксис и, на первый взгляд, кажется более сложным, чем простое процедурное кодирование. Однако, при более тщательном ознакомлении, ООП оказывается в действительности очень чётким и чрезвычайно простым подходом к программированию. Что такое объекты и классы Прежде, чем погрузиться в чёткие определения ООП, необходимо составить общее представление о разнице между классами и объектами. Данный раздел статьи поможет составить представление о классах, их различных возможностях и некоторых применениях. В чем заключается разница между классами и объектами Разработчики, начиная разговаривать о классах и объектах, начинают подменять понятия. К сожалению, такое очень часто происходит. Класс, например, это проект дома. Он определяет на бумаге как будет выглядеть дом, чётко описывает все взаимосвязи между его различными частями, даже если дом не существует в реальности. А объект — это реальный дом, который построен в соответствии с проектом. Данные, которые хранятся в объекте похожи на дерево, провода и бетон, из которых построен дом: без сборки в соответствии с проектом, они будут всего лишь кучей материалов . Однако, собранные вместе они становятся отличным и удобным домом. Классы формируют структуру данных и действий и используют эту информацию для строительства объектов. Из одного класса может быть построено более одного объекта в одно и тоже время, каждый из них будет независим от других. Продолжая аналогию со строительством, целый район может быть построен по одному проекту: 150 различных домов, которые выглядят одинаково, но в каждом из них живут разные семьи и внутренняя отделка строений разная. Структура класса Синтаксис для создания класса очень прост: для объявления класса используется ключевое слово class, за которым следует имя класса и набор фигурных скобок ({}): 1 <?php 2 3 class MyClass 4 { 5 // Свойства и методы класса 6 } 7 8 ?> После создания класса, новый объект может быть реализован и сохранен в переменной с использованием ключевого слова new: 1 $obj = new MyClass; Чтобы увидеть содержимое объекта, используйте var_dump(): 1 var_dump($obj); Можно протестировать весь процесс, скопировав весь код в файл test.php : 01 <?php 02 03 class MyClass 04 { 05 // Свойства и методы класса 06 } 07 08 $obj = new MyClass; 09 10 var_dump($obj); 11 12 ?> Загрузите страницу в ваш браузер и на экране должна появиться следующая строка: 1 object(MyClass)#1 (0) { } Вы только что создали свой первый скрипт ООП. Определение свойств класса Свойства, или переменные класса, используются для того, чтобы добавить данные в класс. Свойства работают как обычные переменные, но они связаны с объектом, и к ним можно получить доступ только используя объект. Чтобы добавить свойства классу MyClass, используйте такой код в вашем скрипте: 01 <?php 02 03 class MyClass 04 { 05 public $prop1 = "Свойство класса "; 06 } 07 08 $obj = new MyClass; 09 10 var_dump($obj); 11 12 ?> Ключевое слово public определяет видимость свойства, мы расскажем о нем чуть позже в данной главе. Затем свойство именуется с использованием обычного синтаксиса для переменных, и ему присваивается значение (хотя свойство класса не нуждается в инициализации). Чтобы прочитать значение свойства и вывести его в браузере, нужно сослаться на объект, из которого будет производиться чтение: 1 echo $obj->prop1; Так как может существовать несколько реализаций класса, то без ссылки на конкретный объект, свойство не может быть прочитано, потому что скрипт не может определить, из какого объекта следует читать. Стрелка (->) - это конструкция ООП, которая используется для получения доступа к свойствам и методам заданного объекта. Модифицируйте скрипт в test.php, чтобы прочитать значение свойства, а не выводить информацию обо всем классе: 01 <?php 02 03 class MyClass 04 { 05 public $prop1 = "Свойство класса "; 06 } 07 08 $obj = new MyClass; 09 10 echo $obj->prop1; // Выводим свойство 11 12 ?> Обновите страницу в вашем браузере, чтобы увидеть результат работы скрипта: 1 Свойство класса Определение методов класса Метод — это функция класса. Индивидуальное действие, которое может выполнить объект, определяется в классе как метод. Например, создадим методы, которые устанавливают и читают значение свойства $prop1: 01 <?php 02 03 class MyClass 04 { 05 public $prop1 = "Свойство класса "; 06 07 public function setProperty($newval) 08 { 09 $this->prop1 = $newval; 10 } 11 12 public function getProperty() 13 { 14 return $this->prop1 . "<br />"; 15 } 16 } 17 18 $obj = new MyClass; 19 20 echo $obj->prop1; 21 22 ?> Примечание — ООП позволяет объекту ссылаться на самого себя, используя $this. При работе внутри метода, использование $this позволяет использовать имя объекта вне класса. Чтобы использовать метод, вызовите его как обычную функцию, но сначала укажите объект, которому она принадлежит. Читаем свойство из MyClass, изменяем значение, читаем его снова: 01 <?php 02 03 class MyClass 04 { 05 public $prop1 = "Свойство класса "; 06 07 public function setProperty($newval) 08 { 09 $this->prop1 = $newval; 10 } 11 12 public function getProperty() 13 { 14 return $this->prop1 . "<br />"; 15 } 16 } 17 18 $obj = new MyClass; 19 20 echo $obj->getProperty(); // получаем значение свойства 21 22 $obj->setProperty("Новое свойство."); // Устанавливаем новое значение 23 24 echo $obj->getProperty(); // Снова читаем значение, чтобы увидеть изменения 25 26 ?> Обновляем страницу в браузере и видим следующее: 1 Свойство класса 2 Новое свойство Преимущества ООП проявляются при использовании множества объектов одного класса. 01 <?php 02 03 class MyClass 04 { 05 public $prop1 = "Свойство класса "; 06 07 public function setProperty($newval) 08 { 09 $this->prop1 = $newval; 10 } 11 12 public function getProperty() 13 { 14 return $this->prop1 . "<br />"; 15 } 16 } 17 18 // Создаем два объекта 19 $obj = new MyClass; 20 $obj2 = new MyClass; 21 22 // Получаем значения $prop1 из обоих объектов 23 echo $obj->getProperty(); 24 echo $obj2->getProperty(); 25 26 // Устанавливаем новые значения свойств для обоих объектов 27 $obj->setProperty("Новое значение свойства"); 28 $obj2->setProperty("Свойство принадлежит второму объекту"); 29 30 // Выводим значения $prop1 для обоих 31 echo $obj->getProperty(); 32 echo $obj2->getProperty(); 33 34 ?> Когда вы загрузите страницу в браузер, то увидите следующее: 1 Свойство класса 2 Свойство класса 3 Новое значение свойства 4 Свойство принадлежит второму объекту Обратите внимание, ООП сохраняет объекты как различные сущности, что позволяет легко разделять код на различные маленькие и взаимосвязанные части. Магические методы в ООП Чтобы сделать использование объектов проще, PHP имеет несколько магических методов. Это специальные методы, которые вызываются, когда над объектом производятся определённые действия. Таким образом разработчик может выполнить несколько общих задач относительно просто. Использование конструкторов и деструкторов Когда создаётся объект, очень часто нужно, чтобы при этом сразу производились установки некоторых свойств. Для выполнения таких задач PHP имеет магический метод __construct(), который вызывается автоматически при создании нового объекта. Для иллюстрации концепции, добавим конструктор к классу MyClass. Он будет выводить сообщение при создании нового объекта класса: 01 <?php 02 03 class MyClass 04 { 05 public $prop1 = "Свойство класса "; 06 07 public function __construct() 08 { 09 echo 'Создан объект класса "'. __CLASS__. '"!<br />'; 10 } 11 12 public function setProperty($newval) 13 { 14 $this->prop1 = $newval; 15 } 16 17 public function getProperty() 18 { 19 return $this->prop1 . "<br />"; 20 } 21 } 22 23 // Создаем новый объект 24 $obj = new MyClass; 25 26 // Получаем значение свойства $prop1 27 echo $obj->getProperty(); 28 29 // Выводим сообщение о достижении конца файла 30 echo "Конец файла.<br />"; 31 32 ?> Примечание — константа __CLASS__ возвращает имя класса, в котором она вызывается; это одна из магических констант PHP. Обновляем страницу в браузере и получаем результат: 1 Создан объект класса "MyClass"! 2 Свойство класса 3 Конец файла. Для вызова функции в процессе удаления объекта используется магический метод __destruct(). Это очень полезный метод для корректной очистки свойств класса (например, для правильного закрытия соединения с базой данных). Выведем сообщение, когда будет удаляться объект класса, с помощью магического метода: __destruct() in MyClass: 01 <?php 02 03 class MyClass 04 { 05 public $prop1 = "Свойство класса "; 06 07 public function __construct() 08 { 09 echo 'Создан объект класса "', __CLASS__, '"!<br />'; 10 } 11 12 public function __destruct() 13 { 14 echo 'Объект класса "', __CLASS__, '" удален.<br />'; 15 } 16 17 public function setProperty($newval) 18 { 19 $this->prop1 = $newval; 20 } 21 22 public function getProperty() 23 { 24 return $this->prop1 . "<br />"; 25 } 26 } 27 28 // Создаём новый объект 29 $obj = new MyClass; 30 31 // Получаем значение свойства $prop1 32 echo $obj->getProperty(); 33 34 // Выводим сообщение о достижении конца файла 35 echo "Конец файла.<br />"; 36 37 ?> Обновляем страницу в браузере и получаем результат: 1 Создан объект класса "MyClass"! 2 Свойство класса 3 Конец файла. 4 Объект класса "MyClass" удален. При достижении конца файла PHP автоматически освобождает все ресурсы. Для явного вызова деструктора и удаления объекта Вы можете использовать функцию unset(): 01 <?php 02 03 class MyClass 04 { 05 public $prop1 = "Свойство класса "; 06 07 public function __construct() 08 { 09 echo 'Создан объект класса "', __CLASS__, '"!<br />'; 10 } 11 12 public function __destruct() 13 { 14 echo 'Объект классса "', __CLASS__, '" удален.<br />'; 15 } 16 17 public function setProperty($newval) 18 { 19 $this->prop1 = $newval; 20 } 21 22 public function getProperty() 23 { 24 return $this->prop1 . "<br />"; 25 } 26 } 27 28 // Создаем новый объект 29 $obj = new MyClass; 30 31 // Получаем значение свойства $prop1 32 echo $obj->getProperty(); 33 34 // Удаляем объект 35 unset($obj); 36 37 // Выводим сообщение о достижении конца файла 38 echo "Конец файла.<br />"; 39 40 ?> Теперь результат работы кода будет выглядеть после загрузки в браузер следующим образом: 1 Создан объект класса "MyClass"! 2 Свойство класса 3 Объект класса "MyClass" удален. 4 Конец файла. Преобразование в строку Чтобы избежать ошибки, если скрипт попытается вывести MyClass как строку, используется другой магический метод __toString(). Без использования __toString() попытка вывести объект как строку приведёт к фатальной ошибке. Попробуйте использовать функцию echo, чтобы вывести объект без применения магического метода: 01 <?php 02 03 class MyClass 04 { 05 public $prop1 = "Свойство класса "; 06 07 public function __construct() 08 { 09 echo 'Создан объект класса "', __CLASS__, '"!<br />'; 10 } 11 12 public function __destruct() 13 { 14 echo 'Объект классса "', __CLASS__, '" удален.<br />'; 15 } 16 17 public function setProperty($newval) 18 { 19 $this->prop1 = $newval; 20 } 21 22 public function getProperty() 23 { 24 return $this->prop1 . "<br />"; 25 } 26 } 27 28 // Создаем новый объект 29 $obj = new MyClass; 30 31 // Выводим объект как строку 32 echo $obj; 33 34 // Удаляем объект 35 unset($obj); 36 37 // Выводим сообщение о достижении конца файла 38 echo "Конец файла.<br />"; 39 40 ?> Результат работы будет выглядеть следующим образом: 1 Создан объект класса "MyClass"! 2 3 Catchable fatal error: Object of class MyClass could not be converted to string in /Applications/XAMPP/xamppfiles/htdocs/testing/test.php on line 40 Чтобы избежать ошибки, используйте метод __toString(): 01 <?php 02 03 class MyClass 04 { 05 public $prop1 = "Свойство класса "; 06 07 public function __construct() 08 { 09 echo 'Создан объект класса "', __CLASS__, '"!<br />'; 10 } 11 12 public function __destruct() 13 { 14 echo 'Объект классса "', __CLASS__, '" удален.<br />'; 15 } 16 17 public function __toString() 18 { 19 echo "Используем метод toString: "; 20 return $this->getProperty(); 21 } 22 23 public function setProperty($newval) 24 { 25 $this->prop1 = $newval; 26 } 27 28 public function getProperty() 29 { 30 return $this->prop1 . "<br />"; 31 } 32 } 33 34 // Создаем новый объект 35 $obj = new MyClass; 36 37 // Выводим объект как строку 38 echo $obj; 39 40 // Удаляем объект 41 unset($obj); 42 43 // Выводим сообщение о достижении конца файла 44 echo "Конец файла.<br />"; 45 46 ?> В этом случае попытка конвертировать объект в строку приведёт к вызову метода getProperty(). Загружаем скрипт в браузер и смотрим на результат работы: 1 Создан объект класса "MyClass"! 2 Используем метод toString: Свойство класса 3 Объект класса "MyClass" удален. 4 Конец файла. Подсказка — В дополнение к магическим методам, которые использовались в данном разделе, существует ещё несколько полезных функций. Чтобы получить полный список магических методов смотрите руководство по PHP. Использование наследования классов Классы могут наследовать методы и свойства от других классов с помощью ключевого слова extends. Например, создадим второй класс, который расширяет возможности MyClass и добавляет метод: 01 <?php 02 03 class MyClass 04 { 05 public $prop1 = "Свойство класса "; 06 07 public function __construct() 08 { 09 echo 'Создан объект класса "', __CLASS__, '"!<br />'; 10 } 11 12 public function __destruct() 13 { 14 echo 'Объект классса "', __CLASS__, '" удален.<br />'; 15 } 16 17 public function __toString() 18 { 19 echo "Используем метод toString: "; 20 return $this->getProperty(); 21 } 22 23 public function setProperty($newval) 24 { 25 $this->prop1 = $newval; 26 } 27 28 public function getProperty() 29 { 30 return $this->prop1 . "<br />"; 31 } 32 } 33 34 class MyOtherClass extends MyClass 35 { 36 public function newMethod() 37 { 38 echo "Из нового метода класса " . __CLASS__ . ".<br />"; 39 } 40 } 41 42 // Создаем новый объект 43 $newobj = new MyOtherClass; 44 45 // Используем новый метод 46 echo $newobj->newMethod(); 47 48 // Используем метод из родительского класса 49 echo $newobj->getProperty(); 50 51 ?> После загрузки скрипта в браузер получим результат: 1 Создан объект класса "MyClass"! 2 Из нового метода класса "MyOtherClass". 3 Свойство класса 4 Объект класса "MyClass" удален. Перегрузка унаследованных свойств и методов Чтобы изменить поведение существующих свойств или методов в новом классе, вы можете просто перегрузить их с помощью повторного объявления в новом классе: 01 <?php 02 03 class MyClass 04 { 05 public $prop1 = "Свойство класса "; 06 07 public function __construct() 08 { 09 echo 'Создан объект класса "', __CLASS__, '"!<br />'; 10 } 11 12 public function __destruct() 13 { 14 echo 'Объект классса "', __CLASS__, '" удален.<br />'; 15 } 16 17 public function __toString() 18 { 19 echo "Используем метод toString: "; 20 return $this->getProperty(); 21 } 22 23 public function setProperty($newval) 24 { 25 $this->prop1 = $newval; 26 } 27 28 public function getProperty() 29 { 30 return $this->prop1 . "<br />"; 31 } 32 } 33 34 class MyOtherClass extends MyClass 35 { 36 public function __construct() 37 { 38 echo "Новый конструктор в классе " . __CLASS__ . ".<br />"; 39 } 40 41 public function newMethod() 42 { 43 echo "Из нового метода класса " . __CLASS__ . ".<br />"; 44 } 45 } 46 47 // Создаем новый объект 48 $newobj = new MyOtherClass; 49 50 // Выводим объект как строку 51 echo $newobj->newMethod(); 52 53 // Используем метод из родительского класса 54 echo $newobj->getProperty(); 55 56 ?> Изменения приведут к следующему выводу при выполнении кода: 1 Новый конструктор в классе "MyOtherClass". 2 Из нового метода класса "MyOtherClass". 3 Свойство класса 4 Объект класса "MyClass" удален. Сохранение оригинальной функциональности при перегрузке методов Чтобы добавить новую функциональность к унаследованному методу при сохранении функций оригинального метода, используйте ключевое слово parent с оператором разрешения видимости (::): 01 <?php 02 03 class MyClass 04 { 05 public $prop1 = "Свойство класса "; 06 07 public function __construct() 08 { 09 echo 'Создан объект класса "', __CLASS__, '"!<br />'; 10 } 11 12 public function __destruct() 13 { 14 echo 'Объект классса "', __CLASS__, '" удален.<br />'; 15 } 16 17 public function __toString() 18 { 19 echo "Используем метод toString: "; 20 return $this->getProperty(); 21 } 22 23 public function setProperty($newval) 24 { 25 $this->prop1 = $newval; 26 } 27 28 public function getProperty() 29 { 30 return $this->prop1 . "<br />"; 31 } 32 } 33 34 class MyOtherClass extends MyClass 35 { 36 public function __construct() 37 { 38 parent::__construct(); // Вызываем конструктор родительского класса 39 echo "Новый конструктор в классе " . __CLASS__ . ".<br />"; 40 } 41 42 public function newMethod() 43 { 44 echo "Из нового метода класса " . __CLASS__ . ".<br />"; 45 } 46 } 47 48 // Создаем новый объект 49 $newobj = new MyOtherClass; 50 51 // Выводим объект как строку 52 echo $newobj->newMethod(); 53 54 // Используем метод из родительского класса 55 echo $newobj->getProperty(); 56 57 ?> Выше приведённый код при выполнении выведет на экран сообщения из обеих конструкторов нового и родительского класса: 1 Создан объект класса "MyClass"! 2 Новый конструктор в классе "MyOtherClass". 3 Из нового метода класса "MyOtherClass". 4 Свойство класса 5 Объект класса "MyClass" удален. Определение области видимости свойств и методов Для дополнительного контроля над объектами, методами и свойствами устанавливается область видимости. Таким образом контролируется как и откуда могут быть доступны свойства и методы. Существует три ключевых слова для установки области видимости: public, protected, и private. В дополнение к установке области видимости, методы и свойства могут быть объявлены как static, что позволяет получать к ним доступ без реализации класса. Примечание — Область видимости — это новое свойство, которое было введено в PHP 5. чтобы узнать о совместимости ООП с PHP 4, смотрите руководство по использованию PHP. Свойства и методы public (Общие) Все свойства и методы, которые вы использовали ранее в данной статье были public (общими). Это значит, что доступ к ним можно было получить где угодно, как внутри класса, так и вне класса.. Методы и свойства protected (Защищённые) Когда свойство или метод объявляется с директивой protected, оно может быть доступно только внутри самого класса или внутри производных классов (классов, которые расширяют базовый класс, содержащий метод с директивой protected). Объявим метод getProperty()как protected в MyClass и попробуем получить доступ к нему вне класса: 01 <?php 02 03 class MyClass 04 { 05 public $prop1 = "Свойство класса "; 06 07 public function __construct() 08 { 09 echo 'Создан объект класса "', __CLASS__, '"!<br />'; 10 } 11 12 public function __destruct() 13 { 14 echo 'Объект классса "', __CLASS__, '" удален.<br />'; 15 } 16 17 public function __toString() 18 { 19 echo "Используем метод toString: "; 20 return $this->getProperty(); 21 } 22 23 public function setProperty($newval) 24 { 25 $this->prop1 = $newval; 26 } 27 28 protected function getProperty() 29 { 30 return $this->prop1 . "<br />"; 31 } 32 } 33 34 class MyOtherClass extends MyClass 35 { 36 public function __construct() 37 { 38 parent::__construct(); 39 echo "Новый конструктор в классе " . __CLASS__ . ".<br />"; 40 } 41 42 public function newMethod() 43 { 44 echo "Из нового метода класса " . __CLASS__ . ".<br />"; 45 } 46 } 47 48 // Создаем новый объект 49 $newobj = new MyOtherClass; 50 51 // Пробуем вызвать защищенный метод 52 echo $newobj->getProperty(); 53 54 ?> При попытке выполнить скрипт, будет сгенерирована следующая ошибка: 1 Создан объект класса "MyClass"! 2 Новый конструктор в классе "MyOtherClass". 3 4 Fatal error: Call to protected method MyClass::getProperty() from context '' in /Applications/XAMPP/xamppfiles/htdocs/testing/test.php on line 55 Теперь, создадим новый метод в MyOtherClass для вызова метода getProperty(): 01 <?php 02 03 class MyClass 04 { 05 public $prop1 = "Свойство класса "; 06 07 public function __construct() 08 { 09 echo 'Создан объект класса "', __CLASS__, '"!<br />'; 10 } 11 12 public function __destruct() 13 { 14 echo 'Объект классса "', __CLASS__, '" удален.<br />'; 15 } 16 17 public function __toString() 18 { 19 echo "Используем метод toString: "; 20 return $this->getProperty(); 21 } 22 23 public function setProperty($newval) 24 { 25 $this->prop1 = $newval; 26 } 27 28 protected function getProperty() 29 { 30 return $this->prop1 . "<br />"; 31 } 32 } 33 34 class MyOtherClass extends MyClass 35 { 36 public function __construct() 37 { 38 parent::__construct(); 39 echo "Новый конструктор в классе " . __CLASS__ . ".<br />"; 40 } 41 42 public function newMethod() 43 { 44 echo "Из нового метода класса " . __CLASS__ . ".<br />"; 45 } 46 47 public function callProtected() 48 { 49 return $this->getProperty(); 50 } 51 } 52 53 // Создаём новый объект 54 $newobj = new MyOtherClass; 55 56 // Вызываем защищённый метод из общего метода 57 echo $newobj->callProtected(); 58 59 ?> При выполнении скрипта результат будет таким: 1 Создан объект класса "MyClass"! 2 Новый конструктор в классе "MyOtherClass". 3 Свойство класса 4 Объект класса "MyClass" удален. Методы и свойства private (Частные) Свойства и методы, объявленные с директивой private, доступны только внутри класса, в котором они определены. Это означает, что даже если новый класс будет производным от класса, в котором определены частные свойства и методы, они не будут доступны в производном классе. Для демонстрации объявим метод getProperty() как private в MyClass, и попробуем вызвать метод callProtected() из MyOtherClass: 01 <?php 02 03 class MyClass 04 { 05 public $prop1 = "Свойство класса "; 06 07 public function __construct() 08 { 09 echo 'Создан объект класса "', __CLASS__, '"!<br />'; 10 } 11 12 public function __destruct() 13 { 14 echo 'Объект классса "', __CLASS__, '" удален.<br />'; 15 } 16 17 public function __toString() 18 { 19 echo "Используем метод toString: "; 20 return $this->getProperty(); 21 } 22 23 public function setProperty($newval) 24 { 25 $this->prop1 = $newval; 26 } 27 28 private function getProperty() 29 { 30 return $this->prop1 . "<br />"; 31 } 32 } 33 34 class MyOtherClass extends MyClass 35 { 36 public function __construct() 37 { 38 parent::__construct(); 39 echo "Новый конструктор в классе " . __CLASS__ . ".<br />"; 40 } 41 42 public function newMethod() 43 { 44 echo "Из нового метода класса " . __CLASS__ . ".<br />"; 45 } 46 47 public function callProtected() 48 { 49 return $this->getProperty(); 50 } 51 } 52 53 // Создаём новый объект 54 $newobj = new MyOtherClass; 55 56 // Используем метод из родительского класса 57 echo $newobj->callProtected(); 58 59 ?> сохраняем скрипт, обновляем страницу в браузере и получаем следующее: 1 Создан объект класса "MyClass"! 2 Новый конструктор в классе "MyOtherClass". 3 4 Fatal error: Call to private method MyClass::getProperty() from context 'MyOtherClass' in /Applications/XAMPP/xamppfiles/htdocs/testing/test.php on line 49 Методы и свойства Static (статические) Методы и свойства, объявленные с директивой static могут быть доступны без инициации класса. Вы просто используете имя класса, оператор разрешения видимости и имя свойства или метода. Одним из основных преимуществ статических свойств является то, что они сохраняют свои значения на протяжении работы всего скрипта. Для демонстрации добавим статическое свойство $count и статический метод plusOne() к классу MyClass. Затем установим цикл do...while для вывода увеличивающего значения $count, до тех пор пока оно не станет больше 10: 01 <?php 02 03 class MyClass 04 { 05 public $prop1 = "Свойство класса "; 06 07 public static $count = 0; 08 09 public function __construct() 10 { 11 echo 'Создан объект класса "', __CLASS__, '"!<br />'; 12 } 13 14 public function __destruct() 15 { 16 echo 'Объект классса "', __CLASS__, '" удален.<br />'; 17 } 18 19 public function __toString() 20 { 21 echo "Используем метод toString: "; 22 return $this->getProperty(); 23 } 24 25 public function setProperty($newval) 26 { 27 $this->prop1 = $newval; 28 } 29 30 private function getProperty() 31 { 32 return $this->prop1 . "<br />"; 33 } 34 35 public static function plusOne() 36 { 37 return "count = " . ++self::$count . ".<br />"; 38 } 39 } 40 41 class MyOtherClass extends MyClass 42 { 43 public function __construct() 44 { 45 parent::__construct(); 46 echo "Новый конструктор в классе " . __CLASS__ . ".<br />"; 47 } 48 49 public function newMethod() 50 { 51 echo "Из нового метода класса " . __CLASS__ . ".<br />"; 52 } 53 54 public function callProtected() 55 { 56 return $this->getProperty(); 57 } 58 } 59 60 do 61 { 62 // Вызываем plusOne без инициации класса MyClass 63 echo MyClass::plusOne(); 64 } while ( MyClass::$count < 10 ); 65 66 ?> Примечание — Для доступа к статическим свойствам знак доллара ($) должен стоять после оператора разрешения видимости. Выполнение скрипта в браузере выдаст следующий результат: 01 count = 1. 02 count = 2. 03 count = 3. 04 count = 4. 05 count = 5. 06 count = 6. 07 count = 7. 08 count = 8. 09 count = 9. 10 count = 10. Комментирование с помощью DocBlock Не являясь официальной частью ООП, комментирование в стиле DocBlock широко используется как метод документирования классов. Кроме того, оно адаптировано для многих популярных пакетов разработчиков, таких как Eclipse и NetBeans, и используется для генерации описания кода. DocBlock определяется с помощью блочного комментария, который имеет в начале дополнительную звездочку: 1 /** 2 * Это базовый DocBlock 3 */ Действительная мощность DocBlocks открывается при использовании тегов, которые начинаются с символа (@) за которым без пробелов следует имя тега и значение. Теги DocBlock позволяют разработчику определять автора файла, лицензию на использование класса, информацию о свойствах и методах, а также много другой полезной информации о коде. Наиболее популярные теги: @author: автор текущего элемента (который может быть классом, файлом, методом, или частью кода). В одном DocBlock могут быть указаны несколько авторов. Формат имени автора: Джони Doe <Джони[email protected]>. @copyright: Тег указывает год и владельца копирайта на текущий элемент. Формат: 2010 Владелец копирайта. @license: Ссылка на тип лицензии текущего элемента. Формат: http://www.example.com/path/to/license.txt Название лицензии. @var: Содержит тип и описание переменной или метода класса. Формат: тип элемент описание. @param: Содержит тип и описание параметров функции или метода. Формат: тип $элемент описание элемента. @return: Тип и описание возвращаемого значения для функции или метода. Формат: тип возвращаемое_значение описание. Простой класс с комментариями в стиле DocBlocks может выглядеть так:: 01 <?php 02 03 /** 04 * Простой класс 05 * 06 * Это длинное описание данного класса, 07 * которое может быть продолжено настолько, на сколько нужно. 08 * 09 * Оно также может быть расширенно несколькими параграфами, 10 * если описание стало слишком многословным. 11 * 12 * @author Jason Lengstorf <[email protected]> 13 * @copyright 2010 Ennui Design 14 * @license http://www.php.net/license/3_01.txt PHP License 3.01 15 */ 16 class SimpleClass 17 { 18 /** 19 * Переменные public 20 * 21 * @var string содержит данные класса 22 */ 23 public $foo; 24 25 /** 26 * Присваиваем $foo новое значение в ходе инициации класса 27 * 28 * @param string $val новое значение 29 * @return void 30 */ 31 public function __construct($val) 32 { 33 $this->foo = $val; 34 } 35 36 /** 37 * Умножаем два целых 38 * 39 * Принимает два целых и возвращает их произведение. 40 * 41 * @param int $bat множимое 42 * @param int $baz множитель 43 * @return int произведение 44 */ 45 public function bar($bat, $baz) 46 { 47 return $bat * $baz; 48 } 49 } 50 51 ?> Преимущество стиля DocBlock очевидны: все чётко определено, так что другой разработчик может легко разобраться в коде. Сравнение объектно-ориентированного и процедурного программирования Нет правильного и ошибочного стиля программирования. Данный раздел описывает сильные аргументы в пользу использования объектно-ориентированного программирования для разработки программного обеспечения, особенно при реализации крупных проектов. Аргумент 1: простота применения Не смотря на то, что ООП сначала приводит в растерянность, оно в действительности обеспечивает более простой подход для работы с данными. Так как объект может хранить данные внутри себя, то нет необходимости предавать переменные от функции к функции для корректной работы. Также, благодаря тому, что множественные реализации одного класса могут существовать одновременно, работа с большими наборами данных становится очень простой. Например, представим что у вас для обработки есть файл с информацией о двух людях. Нужно обрабатывать их имена, род деятельности и возраст. Процедурный подход Ниже приводится процедурный подход для нашего примера: 01 <?php 02 03 function changeJob($person, $newjob) 04 { 05 $person['job'] = $newjob; // Изменяем место работы для человека 06 return $person; 07 } 08 09 function happyBirthday($person) 10 { 11 ++$person['age']; // Добавляем 1 к возрасту человека 12 return $person; 13 } 14 15 $person1 = array( 16 'name' => 'Томми', 17 'job' => 'Старший нажиматель кнопки', 18 'age' => 34 19 ); 20 21 $person2 = array( 22 'name' => 'Джони', 23 'job' => 'Тянутель большого рычага', 24 'age' => 41 25 ); 26 27 // Выводим начальные значения информации о людях 28 echo "<pre> Человек 1: ", print_r($person1, TRUE), "</pre>"; 29 echo "<pre> Человек 2: ", print_r($person2, TRUE), "</pre>"; 30 31 // Томми получил повышение и у него день рождения 32 $person1 = changeJob($person1, 'Главный двигатель коробочек'); 33 $person1 = happyBirthday($person1); 34 35 // У Джони только день рождения (повысят в другой раз) 36 $person2 = happyBirthday($person2); 37 38 // Выводим новый значения информации о людях 39 echo "<pre> Человек 1: ", print_r($person1, TRUE), "</pre>"; 40 echo "<pre> Человек 2: ", print_r($person2, TRUE), "</pre>"; 41 42 ?> Выполнение кода приведёт к следующему результату: 01 Человек 02 ( 03 [name] => Томми 04 [job] => Старший нажиматель кнопки 05 [age] => 34 06 ) 07 Человек 2: Array 08 ( 09 [name] => Джони 10 [job] => Тянутель большого рычага 11 [age] => 41 12 ) 13 Человек 1: Array 14 ( 15 [name] => Томми 16 [job] => Главный двигатель коробочек 17 [age] => 35 18 ) 19 Человек 2: Array 20 ( 21 [name] => Джони 22 [job] => Тянутель большого рычага 23 [age] => 42 24 ) Не смотря на то, что такой код может быть и отличным, разработчику приходится слишком много моментов держать в памяти. Массив атрибутов каждой персоны должен быть передан в процедуру и возвращён из неё при каждом вызове, что составляет обширное поле для ошибок. Желательно, чтобы для разработчика оставалось как можно меньше мест для ошибок. Только действительно нужная информация для конкретной операции должна передаваться в функции. Подход ООП Вот подход ООП для решения нашего примера: 01 <?php 02 03 class Person 04 { 05 private $_name; 06 private $_job; 07 private $_age; 08 09 public function __construct($name, $job, $age) 10 { 11 $this->_name = $name; 12 $this->_job = $job; 13 $this->_age = $age; 14 } 15 16 public function changeJob($newjob) 17 { 18 $this->_job = $newjob; 19 } 20 21 public function happyBirthday() 22 { 23 ++$this->_age; 24 } 25 } 26 27 // Создаем двух новых людей 28 $person1 = new Person("Томми", "Старший нажиматель кнопки", 34); 29 $person2 = new Person("Джони", "Тянутель большого рычага", 41); 30 31 // Выводим начальную информацию о них 32 echo "<pre> Человек 1: ", print_r($person1, TRUE), "</pre>"; 33 echo "<pre> Человек 2: ", print_r($person2, TRUE), "</pre>"; 34 35 // Даем Томми повышение и устанавливаем день рождения 36 $person1->changeJob("Главный двигатель коробочек"); 37 $person1->happyBirthday(); 38 39 // Джони будет праздновать только день рождения 40 $person2->happyBirthday(); 41 42 // Выводим окончательную информацию о людях 43 echo "<pre> Человек 1: ", print_r($person1, TRUE), "</pre>"; 44 echo "<pre> Человек 2: ", print_r($person2, TRUE), "</pre>"; 45 46 ?> Данный код выдаст следующий результат: 01 Person 1: Person Object 02 ( 03 [_name:private] => Томми 04 [_job:private] => Старший нажиматель кнопки 05 [_age:private] => 34 06 ) 07 08 Person 2: Person Object 09 ( 10 [_name:private] => Джони 11 [_job:private] => Тянутель большого рычага 12 [_age:private] => 41 13 ) 14 15 Person 1: Person Object 16 ( 17 [_name:private] => Томми 18 [_job:private] => Главный двигатель коробочек 19 [_age:private] => 35 20 ) 21 22 Person 2: Person Object 23 ( 24 [_name:private] => Джони 25 [_job:private] => Тянутель большого рычага 26 [_age:private] => 42 27 ) В данном случае немного больше кода используется для реализации, но как только классы определены, создание и модификация данных о людях выполняется легко и непринуждённо. Информацию о человеке не нужно предавать или возвращать из метода, в каждый метод передаётся только то, что нужно. В проектах маленького масштаба разница может показаться очень маленькой. Но как только проект начинает расти в размерах, ООП существенно уменьшает нагрузку на разработчика, если применяется правильно. Подсказка — В ООП нуждается далеко не все. Быстрые маленькие функции, которые обрабатывают что-нибудь обычно не нуждаются в организации класса. Используйте разумный подход при выборе между ООП и процедурным подходом. Аргумент 2: лучшая организация Другим преимуществом ООП является то, насколько хорошо код получается организованным для создания пакетов разработчика и каталогов. Каждый класс можно хранить в отдельном файле. А если используется унификация имён классов, то доступ к классу становится чрезвычайно простым. Предположим у вас есть приложение со 150 классами, которые получают имена динамически с помощью контрольного файла в корне вашего приложения. Все 150 классов используют унификацию имён в соответствии с форматом class.classname.inc.php и хранятся в папке inc в вашем приложении. Контроллер может использовать PHP функцию __autoload() для динамического присоединения только тех классов, которые нужны, а не включать все 150 просто на всякий случай: 1 <?php 2 function __autoload($class_name) 3 { 4 include_once 'inc/class.' . $class_name . '.inc.php'; 5 } 6 ?> Использование для хранения каждого класса своего собственного файла делает код более мобильным и простым для многократного использования без дублирования. Аргумент 3: легче обеспечивать поддержку При правильном использовании ООП позволяет получить более компактный код. Поэтому, гораздо проще внести изменения в код, чем разматывать спагетти процедурной реализации. Если определённый массив информации получает новый атрибут, то при процедурном подходе ( в худшем случае) может потребоваться вставлять новый атрибут в каждую функцию, которая использует массив. А приложение ООП может быть изменено простым добавлением нового свойства и нового метода для обработки свойства. Большинство преимуществ, описанных в данном разделе являются продуктом сочетания ООП и DRY программирования. Возможно создать простой для поддержки процедурный код, который не будет ночным кошмаром разработчика, и сделать ужасный проект на принципах ООП. Pro PHP и jQuery являются отличными примерами хороших привычек программирования в сочетании с ООП для генерации кода, который легко читать и модифицировать. Резюме Изучение ООП продвигает вас на следующий уровень в программировании. При правильном использовании ООП помогает Вам создавать легко читаемый, простой для поддержки, портативный код, который сохранит для вас (и для тех, кто работает с вами) часы напряженной работы над проектом.