- Метки урока:
- php
- кодинг
- оптимизация
Полиморфизм в PHP
В обьектно-ориентированном программировании полиморфизм является мощным и фундаментальным инструментом. Он может быть использован для создания более органичной структуры приложения. Данный урок описывает общее понятие полиморфизма и его приложение к PHP.
Что такое полиморфизм?
Полиморфизм - длинное слово для очень простой концепции.
Полиморфизм описывает шаблон в объектно ориентированном программировании, в котором классы имеют различную функциональность при использовании общего интерфейса.
Прелесть полиморфизма заключается в том, что можно работать в коде с различными классами, и при этом не нужно знать, что за класс используется, потому что они имеют один и тот же интерфейс.
Аналогия полиморфизма в реальном мире - кнопка. Каждый знает как использовать кнопку - нужно просто нажать на нее. Но то, что "делает" кнопка в действительности, зависит от ее соединений и контекста использования. Если кто-то говорит, что нужно нажать кнопку, то уже известно, что нужно сделать, чтобы решить задачу.
В мире программирования полиморфизм используется для создания модульных структур приложения и упрощения процедуры расширения функционала. Вместо того, чтобы использовать мешанину условных выражений, описывающих различные варианты действий, можно создать взаимозаменяемые объекты, которые будут выбираться в зависимости от условий использования. Вот в чем заключается основная цель использования полиморфизма.
Интерфейсы
Интегральная часть полиморфизма - общий интерфейс. Существует два способа определить интерфейс в PHP: интерфейс и абстрактный класс. Оба способа имеют свое назначение, их можно использовать совместно или выбирать тот, который лучше подходит к иерархии классов.
Интерфейс
Интерфейс похож на класс, за исключением того, что он не может содержать код. Интерфейс может определять имена методов и аргументов, но не содержание методов. Любой класс, реализующий интерфейс должен реализовать все методы, определенные в интерфейсе. Класс может реализовать несколько интерфейсов.
Интерфейс определяется ключевым словом ‘interface
‘:
interface MyInterface { // Методы }
и присоединяется к классу с помощью ключевого слова ‘implements
‘ (несколько интерфейсов могут быть использованы с помощью указания их один за другим через запятую):
class MyClass implements MyInterface { // methods }
Методы можно определять в интерфейсе также как и в классе, только без тела функции (части между фигурными скобками):
interface MyInterface { public function doThis(); public function doThat(); public function setName($name); }
Все методы, определенные в интерфейсе, должны быть реализованы в реализующем интерфейс классе. Причем методы обязательно должны быть публичными и в точности соответствовать определению в интерфейсе (смотри пример ниже)
// ПРАВИЛЬНО class MyClass implements MyInterface { protected $name; public function doThis() { // код метода } public function doThat() { // код метода } public function setName($name) { $this->name = $name; } } // НЕПРАВИЛЬНО class MyClass implements MyInterface { // missing doThis()! private function doThat() { // метод обязательно должен быть публичным! } public function setName() { // пропущен аргумент! } }
Абстрактный класс
Абстрактный класс является смесью интерфейса и обычного класса. Он может определять функциональность также, как и интерфейс (в форме абстрактных методов). Класс, расширяющий абстрактный класс, должен содержать реализацию всех абстрактных методов, определенных в абстрактном классе.
Абстрактный класс определяется также, как и обычный класс, но с добавлением ключевого слова ‘abstract
‘:
abstract class MyAbstract { // Методы }
и он присоединяется к классу с помощью ключевого слова ‘extends
‘:
class MyClass extends MyAbstract { // Методы класса }
Обычные методы определяются в абстрактном классе также, как и в обычном классе, а абстрактные методы определяются с использованием ключевого слова ‘abstract
‘. Абстрактные методы используются также как в интерфейсе и должны быть реализованы в расширяющем классе.
abstract class MyAbstract { public $name; public function doThis() { // код функции } abstract public function doThat(); abstract public function setName($name); }
Шаг 1: Описание проблемы
Допустим у нас есть класс Article
, который управляет статьями на сайте. Он содержит информацию о статье, включая заголовок, автора, дату создания и категорию. Класс выглядит примерно так::
class poly_base_Article { public $title; public $author; public $date; public $category; public function __construct($title, $author, $date, $category = 0) { $this->title = $title; $this->author = $author; $this->date = $date; $this->category = $category; } }
Примечание: Примеры классов в данном уроке будут использовать соглашение о наименовании “пакет_компонент_Класс”. Таким образом будут разделяться классы в виртуальном пространстве имен, чтобы избежать коллизий.
Теперь надо добавить методы для вывода информации в разных форматах, таких как XML и JSON. Есть очень большой соблазн сделать вот так:
class poly_base_Article { //... public function write($type) { $ret = ''; switch($type) { case 'XML': $ret = '<article>'; $ret .= '<title>' . $obj->title . '</title>'; $ret .= '<author>' . $obj->author . '</author>'; $ret .= '<date>' . $obj->date . '</date>'; $ret .= '<category>' . $obj->category . '</category>'; $ret .= '</article>'; break; case 'JSON': $array = array('article' => $obj); $ret = json_encode($array); break; } return $ret; } }
Но такое решение является убогим, хотя оно и будет работать в текущий момент. Спросите себя, что произойдет в будущем, когда нужно будет добавить еще какой-нибудь формат вывода? Вы будете редактировать класс, добавляя все больше и больше выражений case,
и таким образом утяжелите код класса.
Один из принципов объектно ориентированного программирования гласит, что класс должен делать единственную операцию, но он должен делать ее очень качественно.
В нашем примере очевидно смешение двух различных задач: управление статьями и форматирование их данных. Поэтому в рамках данного урока изменим код так, чтобы получить новый набор классов, а заодно посмотрим, как легко это сделать используя полиморфизм.
Шаг 2: Определяем интерфейс
Первым делом надо определить интерфейс. Очень важно хорошо продумать структуру интерфейса, потому что любые изменения кода интерфейса потребуют изменений всех классов, где он будет использоваться. В нашем примере мы будем использовать простой интерфейс для определения одного метода:
interface poly_writer_Writer { public function write(poly_base_Article $obj); }
Все очень просто. Мы определили публичный метод write()
, который принимает в качестве аргумента объект статьи. Любой класс, который реализует наш интерфейс определенно должен иметь метод вывода.
Совет: Если вы хотите ограничить тип аргумента, который будет передаваться в ваши функции и методы, вы можете использовать явное указание типа аргумента, как это сделано в методе write().
Он принимает только объекты типа poly_base_Article
. К сожалению, явно указать тип возвращаемого значения в текущей версии PHP нельзя, поэтому нужно быть очень внимательным с возвращаемыми значениями.
Шаг 3: Создаем реализацию
После определения интерфейса нужно создать класс, который будет выполнять настоящие действия. В нашем примере у нас есть два формата, которые используются для вывода содержания статьи. Таким образом мы имеем два класса: XMLWriter и JSONWriter. Они извлекают данные из переданной статьи и форматируют информацию.
Вот код XMLWriter:
class poly_writer_XMLWriter implements poly_writer_Writer { public function write(poly_base_Article $obj) { $ret = '<article>'; $ret .= '<title>' . $obj->title . '</title>'; $ret .= '<author>' . $obj->author . '</author>'; $ret .= '<date>' . $obj->date . '</date>'; $ret .= '<category>' . $obj->category . '</category>'; $ret .= '</article>'; return $ret; } }
В определении класса используется ключевое слово implements
для реализации нашего интерфейса. Метод write()
содержит код преобразования в XML.
А вот код класса JSONWriter:
class poly_writer_JSONWriter implements poly_writer_Writer { public function write(poly_base_Article $obj) { $array = array('article' => $obj); return json_encode($array); } }
Весь код соответствующий определенному формату теперь содержится в своем индивидуальном классе. Каждый такой класс теперь отвечает только за обработку соответствующего формата и ничего больше. Никакие другие части нашего приложения не задействованы в данном процессе, благодаря использованию интерфейса.
Шаг 4: Используем наши реализации
Теперь имея определения новых классов надо провести ревизию класса статьи. Весь код, который был помещен в оригинальный метод write(),
был реализован в наших новых классах. Все методы должны теперь использовать новые классы:
class poly_base_Article { //... public function write(poly_writer_Writer $writer) { return $writer->write($this); } }
Все методы теперь используются через класс Writer (любой класс, который реализует интерфейс Writer), с помощью вызова метода write()
, с переданным ему $this
в качестве аргумента, а возвращаемое значение используется прямо в коде. Больше не нужно беспокоиться о форматировании данных и можно сконцентрироваться на основной задаче.
Получаем объект Writer
Но как получить объект Writer, который будет выполнять данный метод? Все зависит от вас, и существует много различных стратегий. Например, вы можете создать класс фабрику для перехвата данных запроса и создания объекта:
class poly_base_Factory { public static function getWriter() { // перехватываем переменную запроса $format = $_REQUEST['format']; // конструируем имя нашего класса и проверяем его существование $class = 'poly_writer_' . $format . 'Writer'; if(class_exists($class)) { // возвращаем новый объект Writer return new $class(); } // иначе выдаем сообщение об ошибке throw new Exception('Не поддерживаемый формат'); } }
В данном примере переменная запроса выбирает формат для использования. Мы конструируем имя класса, проверяем его существование и возвращаем новый объект Writer. А если сконструированного имени не существует, то генерируется исключение, чтобы код клиента мог корректно обработать ситуацию.
Шаг 5: Соединяем все вместе
Код, который соединяет функциональность в единое целое может выглядеть примерно так:
$article = new poly_base_Article('Polymorphism', 'Steve', time(), 0); try { $writer = poly_base_Factory::getWriter(); } catch (Exception $e) { $writer = new poly_writer_XMLWriter(); } echo $article->write($writer);
Сначала создается объект Article. Затем мы пытаемся получить объект Writer с помощью Factory, если генерируется исключение то используется формат по умолчанию (XMLWriter). В завершении мы передаем объект Writer методу write()
нашего объекта Article для вывода результата.
Заключение
Данный урок является введением в полиморфизм и описание интерфейсов в PHP. В примере был показан один из возможных способов потенциального использования полиморфизма. Он является элегантным способом избежать использования громоздких условных выражений в объектно ориентированном коде. полиморфизм помогает следовать принципу разделения компонентов и является интегральной частью многих шаблонов программирования.
Данный урок подготовлен для вас командой сайта ruseller.com
Источник урока: net.tutsplus.com/tutorials/php/understanding-and-applying-polymorphism-in-php/
Перевел: Сергей Фастунов
Урок создан: 20 Сентября 2010
Просмотров: 50813
Правила перепечатки
5 последних уроков рубрики "PHP"
-
Фильтрация данных с помощью zend-filter
Когда речь идёт о безопасности веб-сайта, то фраза "фильтруйте всё, экранируйте всё" всегда будет актуальна. Сегодня поговорим о фильтрации данных.
-
Контекстное экранирование с помощью zend-escaper
Обеспечение безопасности веб-сайта — это не только защита от SQL инъекций, но и протекция от межсайтового скриптинга (XSS), межсайтовой подделки запросов (CSRF) и от других видов атак. В частности, вам нужно очень осторожно подходить к формированию HTML, CSS и JavaScript кода.
-
Подключение Zend модулей к Expressive
Expressive 2 поддерживает возможность подключения других ZF компонент по специальной схеме. Не всем нравится данное решение. В этой статье мы расскажем как улучшили процесс подключение нескольких модулей.
-
Совет: отправка информации в Google Analytics через API
Предположим, что вам необходимо отправить какую-то информацию в Google Analytics из серверного скрипта. Как это сделать. Ответ в этой заметке.
-
Подборка PHP песочниц
Подборка из нескольких видов PHP песочниц. На некоторых вы в режиме online сможете потестить свой код, но есть так же решения, которые можно внедрить на свой сайт.