Рассылка закрыта
При закрытии подписчики были переданы в рассылку "Заметки Дизайнера" на которую и рекомендуем вам подписаться.
Вы можете найти рассылки сходной тематики в Каталоге рассылок.
Абстрактные классы. Интерфейсы
PHP 5: Новые возможности.
The Pterodactyl
Copyright 2006 Pterodactyl's School
Copyright 2005 Pterodactyl's School.
Permission is granted to copy, distribute and/or modify this document under the terms of the GNU Free Documentation License, Version 1.2 or any later version published by the Free Software Foundation; with no Invariant Sections, no Front-Cover Texts, and no Back-Cover Texts. A copy of the license is included in the section entitled "GNU Free Documentation License".
03.02.2006
История переиздания | |
---|---|
Издание 1.00 | 03.02.2006 |
Первоначальная версия. |
Аннотация
На конкретном примере рассмотрена концепция абстрактных классов, абстрактных методов и интерфейсов. Продолжена практическая разработка учебного приложения.
С концепцией абстрактных классов познакомимся на следующем примере. Допустим, разрабатывается объектно-ориентированный пакет для отправки электронной почты (назовем его Mailer). Этот пакет должен выполнять проверку адресов, компоновку сообщений и прочие универсальные действия. Все эти действия следует вынести в главный класс Mailer
.
С другой стороны, представляется резонным создать отдельные классы для каждого конкретного механизма отправки почты, поддерживаемого приложением, унаследовав их от класса Mailer
; например, Mailer_Mail
, Mailer_Sendmail
и Mailer_SMTP
. Понятно, что каждый из этих классов должен иметь некий метод для отправки корреспонденции, желательно, со стандартным названием (пусть это будет
метод send
). Однако, непосредственная реализация данного метода будет своей для каждого класса.
Такое решение в стиле PHP 4 уже вполне работоспособно, но еще не совсем совершенно. Действительно, обязательность наличия в унаследованных классах метода send
является всего лишь договоренностью и никак не контролируется средствами языка.
В PHP 5 появляется возможность устранить отмеченный недостаток, объявив класс абстрактным. Для этого используется ключевое слово abstract
.
abstract class Mailer { /* ... */ }
Преимущество этого решения станет понятным немного позже.
Внимание
Нельзя создать объект абстрактного класса, попытка сделать это вызовет фатальную ошибку. Абстрактный класс может быть только унаследован.
Абстрактный класс может содержать как определения обычных методов, так и объявления абстрактных методов (см. «Абстрактные методы») - впрочем, и то, и другое не обязательно. Однако, если имеется хотя бы один абстрактный метод, то и сам класс должен быть объявлен абстрактным.
Как и классы, абстрактные методы объявляют при помощи ключевого слова abstract
, которое предшествует модификатору доступа. Такие методы не могут быть определены, то есть, иметь тело с реализующим их кодом. Технически это означает, что после названия метода и круглых скобок вместо открывающей фигурной скобки следует точка с запятой, которая и завершает объявление абстрактного метода.
abstract protected function send();
Абстрактные методы не могут быть объявлены вне абстрактного класса и должны быть вновь объявлены и определены уже как обычные в классе-наследнике (если только тот сам не является абстрактным). При этом их видимость (доступность) не может быть меньшей, чем в родительском классе (но может быть большей). Любое нарушение этих правил вызовет фатальную ошибку.
Таким образом, обычное соглашение о намерениях превращается в прочное обязательство, нарушение которого приводит к прекращению работоспособности скрипта; при этом непосредственная реализация метода откладывается до того момента, когда она будет иметь смысл (Пример 1, «Использование абстрактного класса Mailer
»). Немаловажно и то, что разработчик более не должен самостоятельно контролировать
соблюдение данного обязательства.
Пример 1. Использование абстрактного класса Mailer
<?php abstract class Mailer { protected $to = 'you@example.com'; protected $subject = 'Test'; protected $message = 'Just a test.'; abstract protected function send(); } class Mailer_Mail extends Mailer { public function send() { mail($this->to, $this->subject, $this->message); } } $mailer = new Mailer_Mail; $mailer->send(); ?>
PHP 5 предлагает также и другой механизм стандартизации приложений - интерфейсы. Они во многом напоминают абстрактные классы и методы. Рассмотрим этот механизм более подробно, применив его к нашему примеру.
Объявление интерфейса производится при помощи ключевого слова interface
.
interface iMailer { /* ... */ }
В отличие от класса, интерфейс может содержать только объявления констант и прототипов методов (то есть, их объявления без определения, как для абстрактных методов, но без ключевого слова abstract
). Кроме того, все методы могут быть только публичными (public
).
public function send();
Далее интерфейс может быть применен к тому или иному классу при помощи оператора implements
[1].
class Mailer_Mail extends Mailer implements iMailer { /* ... */ }
Это означает, что класс (в данном случае, Mailer_Mail) должен обязательно реализовать те методы, которые объявлены в интерфейсе (в данном случае, метод send
) - или же класс должен быть объявлен как абстрактный, а указанные методы реализованы при его наследовании (Пример 2, «Использование интерфейса iMailer (два варианта)»).
Пример 2. Использование интерфейса iMailer (два варианта)
<?php interface iMailer { public function send(); } class Mailer { protected $to = 'you@example.com'; protected $subject = 'Test'; protected $message = 'Just a test.'; } class Mailer_Mail extends Mailer implements iMailer { public function send() { mail($this->to, $this->subject, $this->message); } } $mail = new Mailer_Mail; $mail->send(); ?>
<?php interface iMailer { public function send(); } abstract class Mailer implements iMailer { protected $to = 'you@example.com'; protected $subject = 'Test'; protected $message = 'Just a test.'; } class Mailer_Mail extends Mailer { public function send() { mail($this->to, $this->subject, $this->message); } } $mail = new Mailer_Mail; $mail->send(); ?>
Важным преимуществом интерфейсов является то, что один и тот же класс может реализовывать сразу несколько интерфейсов (в этом случае их названия перечисляются через запятую после оператора implements
)[2]. Другой интересной особенностью является применимость к интерфейсам механизма наследования.
Внимание
Как в случае одновременной реализации нескольких интерфейсов, так и в случае их наследования, необходимо следить, чтобы названия методов и констант, объявленных в разных интерфейсах, не совпадали между собой.
На текущем практическом занятии доработаем наше учебное приложение с учетом полученных знаний, а также продолжим знакомство с пакетом HTML_Template_Flexy. Для этого нам предстоит решить следующие задачи.
- Подготовка конфигурационного файла.
- Инкапсуляция работы с пакетом HTML_Template_Flexy в классе
SSS_Controller
. - Инкапсуляция подготовки и отображения содержимого страницы путем доработки класса
SSS
. - Разработка класса
SSSEx
в качестве примера конкретного решения. - Приведение демонстрационного скрипта в соответствие со сделанными изменениями.
Основная идея всех этих действий - абстрагизация класса SSS
в качестве своего рода универсального инструмента, на основе которого можно было бы легко создавать классы-наследники для построения конкретных сайтов.
Создание конфигурационного файла. До сих пор данные о директориях, в которых находятся исходные и скомпилированные шаблоны, мы передавали конструктору класса HTML_Template_Flexy
при создании нового объекта из демонстрационного скрипта. Однако, удобнее (и правильнее) поместить эти и, если понадобится, другие данные о конфигурации в отдельный файл. Для этого воспользуемся стандартным форматом .ini
файлов, используемых в PHP,
и отнюдь не оригинальным названием (Пример 3, «config.ini
»). Поскольку настройки могут понадобиться и для других компонентов, проявим дальновидность, сделав этот файл многосекционным (настройки для шаблонов будут в секции [HTML_Template_Flexy]
). Договоримся размещать файл с настройками в начальной директории разрабатываемого приложения (в нашем случае - в одной директории с демонстрационным
скриптом).
Этот файл на сайте можно найти здесь (настройки на сайте несколько отличаются от приведенных).
Разработка управляющего класса. Обычно для создания объекта класса HTML_Template_Flexy
и последующей работы с ним используется специальный управляющий класс (назовем его SSS_Controller
). В конструкторе класса прочитаем настройки для шаблонов из только что созданного конфигурационного файла и применим их; публичный метод make
выполнит всю остальную работу и вернет результат (Создание класса SSS_Controller
). Окончательный итог - см. Пример 4, «SSS/Controller.php
».
Процедура 1. Создание класса SSS_Controller
-
Включите класс
HTML_Template_Flexy
.require_once 'HTML/Template/Flexy.php';
-
Объявите класс
SSS_Controller
.class SSS_Controller { /* ... */ }
-
Разработайте конструктор
-
Объявите конструктор, принимающий в качестве аргумента название конфигурационного файла (по умолчанию - '
config.ini
').function __construct($inifile = 'config.ini')
-
Прочитайте файл конфигурации.
$config = parse_ini_file($inifile, TRUE);
-
Получите ссылку на статическую переменную
options
классаHTML_Template_Flexy
.$options =& PEAR::getStaticProperty('HTML_Template_Flexy', 'options');
-
Установите считанные настройки.
$options = $config['HTML_Template_Flexy'];
-
-
Разработайте метод
make
-
Объявите метод. Аргументы: название шаблона; данные для подстановки (по ссылке, по умолчанию -
null
); опции (по умолчанию - пустой массив).public function make($template, &$data = null, $options = array())
-
Создайте новый объект класса
HTML_Template_Flexy
, скомпилируйте шаблон и возвратите результат подстановки данных.$flexy = new HTML_Template_Flexy($options); $flexy->compile($template); return $flexy->bufferedOutputObject($data);
-
-
Сохраните файл
-
Создайте новую директорию
SSS
в одной директории с файломSSS.php
./ |-classes/ |-SSS/ |-SSS.php
-
Сохраните получившийся код в файле
SSS/Controller.php
./ |-classes/ |-SSS/ |-Controller.php |-SSS.php
-
Пример 4. SSS/Controller.php
<?php require_once 'HTML/Template/Flexy.php'; class SSS_Controller { function __construct($inifile = 'config.ini') { $config = parse_ini_file($inifile, TRUE); $options =& PEAR::getStaticProperty('HTML_Template_Flexy', 'options'); $options = $config['HTML_Template_Flexy']; } public function make($template, &$data = null, $options = array()) { $flexy = new HTML_Template_Flexy($options); $flexy->compile($template); return $flexy->bufferedOutputObject($data); } } ?>
Полную версию этого файла (со встроенной документацией) на сайте можно найти здесь.
Доработка класса SSS
. Основное новшество касается добавления абстрактного метода, возвращающего данные для шаблона; вводятся также две новые опции (Доработка класса SSS
до версии 0.04). Наибольшие изменения претерпит конструктор (Пример 5, «Конструктор класса SSS
0.04
»).
Процедура 2. Доработка класса SSS
до версии 0.04
-
Исправьте номер версии.
* @version 0.04
const VERSION = 0.04;
-
Объявите класс абстрактным.
/** * Advanced Abstract (X)HTML Page Generation Class * * Features: * + PEAR Code Standards compliance */ abstract class SSS extends HTML_Page2 {
-
Объявите абстрактный защищенный метод
getContentData
./** * Prepares content data for the template * Returns the stdClass object * Must be overridden in the children class * * @access protected * @return object */ abstract protected function getContentData();
-
Добавьте поддержку дополнительных опций для конфигурационного файла и шаблона.
private $options = array('stylesheet' => '/styles.css', 'favicon' => '/favicon.ico', 'title' => 'SSS', 'generator' => 'Simple Site Solution', 'config' => 'config.ini', 'template' => 'template.htm');
* Supported extra attributes: * + stylesheet Style sheet (addStyleSheet) * + favicon Favorite icon (addFavicon) * + generator Page generator (setMetaData) * + title Page title (setTitle) * + config Config file * + template Template file
* @todo throw exception on config error * @todo throw exception on template error (?)
if (!$this->isFileExists($opt['config'])) { unset($opt['config']); }
-
Включите класс
SSS_Controller
./** * Include other required classes */ require_once 'SSS/Controller.php';
-
Создайте в конструкторе новый объект класса
SSS_Constructor
, передав ему в качестве аргумента опцию с названием файла конфигурации.$controller = new SSS_Controller($opt['config']);
-
Обработайте шаблон и установите тело страницы.
$data = $this->getContentData(); $body = $controller->make($opt['template'], $data); $this->setBody($body);
-
Отобразите страницу.
$this->display();
Пример 5. Конструктор класса SSS 0.04
/** * Class constructor * * Accepts an array of attributes * (just as the parent) and options * Supported extra attributes: * + stylesheet Style sheet (addStyleSheet) * + favicon Favorite icon (addFavicon) * + generator Page generator (setMetaData) * + title Page title (setTitle) * + config Config file * + template Template file * * Calls the parent's constructor * Overrides page attributes * * @param array $attributes Page attributes * @param array $options Extra page attributes * @since 0.02 * @todo throw exception on config error * @todo throw exception on template error (?) */ function __construct($attributes = array(), $options = array()) { $attr = array_merge($this->attributes, $attributes); parent::__construct($attr); $opt = array_merge($this->options, $options); $this->setTitle($opt['title']); $this->setMetaData('Generator', $opt['generator']); if ($this->isFileExists($opt['stylesheet'])) { $this->addStyleSheet($opt['stylesheet']); } if ($this->isFileExists($opt['favicon'])) { $this->addFavicon($opt['favicon']); } if (!$this->isFileExists($opt['config'])) { unset($opt['config']); } $controller = new SSS_Controller($opt['config']); $data = $this->getContentData(); $body = $controller->make($opt['template'], $data); $this->setBody($body); $this->display(); } // end class constructor
Полностью листинг класса SSS 0.04
(со встроенной документацией) на сайте можно найти здесь.
Разработка класса SSSEx
. Класс SSSEx
(Simple Site Solution Example) призван стать своеобразной прослойкой между использующим его скриптом на уровне конечного пользователя и классом SSS
, предоставляющим интерфейс для создания страницы на более высоком уровне. Акцент при дальнейшей разработке нашего учебного примера сместится именно на
этот класс. Его конструктор производит некоторые настройки (ранее этим занимался демонстрационный скрипт); метод же getContentData
будет готовить данные для подстановки в шаблон страницы, а пока что возвращает пустой объект (Пример 6, «Класс SSSEx
»).
Замечание
Класс SSSEx
не является частью пакета SSS.
Пример 6. Класс SSSEx
<?php require_once 'SSS.php'; class SSSEx extends SSS { function __construct() { $attributes = array('language' => 'ru', 'charset' => 'windows-1251'); $options = array('title' => 'SSS 0.04 Demo Page', 'template' => 'index.htm'); parent::__construct($attributes, $options); } protected function getContentData() { $data = new stdClass; /* ... */ return $data; } } ?>
Этот файл на сайте можно найти здесь (некоторые опции на сайте могут отличаться от приведенных).
Изменение демонстрационного скрипта. После скрытия всех рутинных деталей внутри классов демонстрационный скрипт сократился до абсолютно возможного минимума (Пример 7, «Демонстрационный скрипт»). Не правда ли, идеальное решение для конечного пользователя?
Пример 7. Демонстрационный скрипт
<?php ///////////////////////////////////////////////////////// $include_path[] = get_include_path(); $include_path[] = $_SERVER['DOCUMENT_ROOT'].'/pear'; $include_path[] = $_SERVER['DOCUMENT_ROOT'].'/classes'; set_include_path(implode(PATH_SEPARATOR, $include_path)); ///////////////////////////////////////////////////////// require_once 'SSSEx.php'; $page = new SSSEx; ?>
Этот файл на сайте можно найти здесь.
Выведенная страница в этот раз почти ничем не отличается от предыдущей версии, кроме, разве что номера самой версии (Пример 8, «Результат работы демонстрационного скрипта»). Зато получение этой страницы значительно упростилось.
Пример 8. Результат работы демонстрационного скрипта
Этот шаблон со временем будет наполнен реальным содержимым.
<?xml version="1.0" encoding="windows-1251"?> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3c.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="ru"> <head> <meta name="Generator" content="Simple Site Solution" /> <title>SSS 0.04 Demo Page</title> <link href="/favicon.ico" rel="shortcut icon" type="image/x-icon" /> <link rel="stylesheet" href="/styles.css" type="text/css" /> </head> <body> <p>Этот шаблон со временем будет наполнен реальным содержимым.</p> </body> </html>
[1] PHP Manual. http://www.php.net/manual/en/. Gabor Hojtsy. 10-01-2006. Copyright 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006 .
[2] PEAR Manual. http://pear.php.net/manual/en/. Daniel Convissor. Martin Jansen. Alexander Merz. 25-12-2005. Copyright 2001, 2002, 2003, 2004, 2005, 2006 The PEAR Documentation Group.
С последней версией этой и других статей Вы можете ознакомиться на сайте рассылки [http://pterodactyl.l2p.net/php5/].
Subscribe.Ru
Поддержка подписчиков Другие рассылки этой тематики Другие рассылки этого автора |
Подписан адрес:
Код этой рассылки: inet.webbuild.php5whatsnew Архив рассылки |
Отписаться
Вебом
Почтой
Вспомнить пароль |
В избранное | ||