Отправляет email-рассылки с помощью сервиса Sendsay

PHP 5: Новые возможности.

  Все выпуски  

Абстрактные классы. Интерфейсы


Информационный Канал Subscribe.Ru

PHP 5: Новые возможности.

The Pterodactyl

Copyright 2006 Pterodactyl's School

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»). Немаловажно и то, что разработчик более не должен самостоятельно контролировать соблюдение данного обязательства.

PHP 5 предлагает также и другой механизм стандартизации приложений - интерфейсы. Они во многом напоминают абстрактные классы и методы. Рассмотрим этот механизм более подробно, применив его к нашему примеру.

Объявление интерфейса производится при помощи ключевого слова interface.

interface iMailer { /* ... */ }

В отличие от класса, интерфейс может содержать только объявления констант и прототипов методов (то есть, их объявления без определения, как для абстрактных методов, но без ключевого слова abstract). Кроме того, все методы могут быть только публичными (public).

public function send();

Далее интерфейс может быть применен к тому или иному классу при помощи оператора implements[1].

class Mailer_Mail extends Mailer implements iMailer { /* ... */ }

Это означает, что класс (в данном случае, Mailer_Mail) должен обязательно реализовать те методы, которые объявлены в интерфейсе (в данном случае, метод send) - или же класс должен быть объявлен как абстрактный, а указанные методы реализованы при его наследовании (Пример 2, «Использование интерфейса iMailer (два варианта)»).

Важным преимуществом интерфейсов является то, что один и тот же класс может реализовывать сразу несколько интерфейсов (в этом случае их названия перечисляются через запятую после оператора implements)[2]. Другой интересной особенностью является применимость к интерфейсам механизма наследования.

Внимание

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

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

  1. Подготовка конфигурационного файла.
  2. Инкапсуляция работы с пакетом HTML_Template_Flexy в классе SSS_Controller.
  3. Инкапсуляция подготовки и отображения содержимого страницы путем доработки класса SSS.
  4. Разработка класса SSSEx в качестве примера конкретного решения.
  5. Приведение демонстрационного скрипта в соответствие со сделанными изменениями.

Основная идея всех этих действий - абстрагизация класса 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

  1. Включите класс HTML_Template_Flexy.

    require_once 'HTML/Template/Flexy.php';
  2. Объявите класс SSS_Controller.

    class SSS_Controller { /* ... */ }
  3. Разработайте конструктор

    1. Объявите конструктор, принимающий в качестве аргумента название конфигурационного файла (по умолчанию - 'config.ini').

      function __construct($inifile = 'config.ini')
    2. Прочитайте файл конфигурации.

      $config = parse_ini_file($inifile, TRUE);
    3. Получите ссылку на статическую переменную options класса HTML_Template_Flexy.

      $options =& PEAR::getStaticProperty('HTML_Template_Flexy',
          'options');
                              
    4. Установите считанные настройки.

      $options = $config['HTML_Template_Flexy'];
  4. Разработайте метод make

    1. Объявите метод. Аргументы: название шаблона; данные для подстановки (по ссылке, по умолчанию - null); опции (по умолчанию - пустой массив).

      public function make($template, &$data = null,
          $options = array())
                              
    2. Создайте новый объект класса HTML_Template_Flexy, скомпилируйте шаблон и возвратите результат подстановки данных.

      $flexy = new HTML_Template_Flexy($options);
      $flexy->compile($template);
      return $flexy->bufferedOutputObject($data);
                              
  5. Сохраните файл

    1. Создайте новую директорию SSS в одной директории с файлом SSS.php.

      /
      |-classes/
        |-SSS/
        |-SSS.php
                              
    2. Сохраните получившийся код в файле SSS/Controller.php.

      /
      |-classes/
        |-SSS/
          |-Controller.php
        |-SSS.php
                              

Полную версию этого файла (со встроенной документацией) на сайте можно найти здесь.

Доработка класса SSS. Основное новшество касается добавления абстрактного метода, возвращающего данные для шаблона; вводятся также две новые опции (Доработка класса SSS до версии 0.04). Наибольшие изменения претерпит конструктор (Пример 5, «Конструктор класса SSS 0.04»).

Процедура 2. Доработка класса SSS до версии 0.04

  1. Исправьте номер версии.

     * @version    0.04
        const VERSION = 0.04;
  2. Объявите класс абстрактным.

    
    /**
     * Advanced Abstract (X)HTML Page Generation Class
     *
     * Features:
     *  + PEAR Code Standards compliance
     */
    abstract class SSS extends HTML_Page2 {
                    
  3. Объявите абстрактный защищенный метод 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();
                    
  4. Добавьте поддержку дополнительных опций для конфигурационного файла и шаблона.

        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']);
            }
                    
  5. Включите класс SSS_Controller.

    /**
     * Include other required classes
     */
    require_once 'SSS/Controller.php';
                    
  6. Создайте в конструкторе новый объект класса SSS_Constructor, передав ему в качестве аргумента опцию с названием файла конфигурации.

            $controller = new SSS_Controller($opt['config']);
  7. Обработайте шаблон и установите тело страницы.

            $data = $this->getContentData();
            $body = $controller->make($opt['template'], $data);
            $this->setBody($body);
                    
  8. Отобразите страницу.

            $this->display();

Полностью листинг класса SSS 0.04 (со встроенной документацией) на сайте можно найти здесь.

Разработка класса SSSEx. Класс SSSEx (Simple Site Solution Example) призван стать своеобразной прослойкой между использующим его скриптом на уровне конечного пользователя и классом SSS, предоставляющим интерфейс для создания страницы на более высоком уровне. Акцент при дальнейшей разработке нашего учебного примера сместится именно на этот класс. Его конструктор производит некоторые настройки (ранее этим занимался демонстрационный скрипт); метод же getContentData будет готовить данные для подстановки в шаблон страницы, а пока что возвращает пустой объект (Пример 6, «Класс SSSEx»).

Замечание

Класс SSSEx не является частью пакета SSS.

Этот файл на сайте можно найти здесь (некоторые опции на сайте могут отличаться от приведенных).

Изменение демонстрационного скрипта. После скрытия всех рутинных деталей внутри классов демонстрационный скрипт сократился до абсолютно возможного минимума (Пример 7, «Демонстрационный скрипт»). Не правда ли, идеальное решение для конечного пользователя?

Этот файл на сайте можно найти здесь.

Выведенная страница в этот раз почти ничем не отличается от предыдущей версии, кроме, разве что номера самой версии (Пример 8, «Результат работы демонстрационного скрипта»). Зато получение этой страницы значительно упростилось.

Created with DocBook Created with Libxslt



[1] implement (англ.) - выполнять, осуществлять.

[2] Это позволяет компенсировать отсутствие в PHP множественного наследования.

С последней версией этой и других статей Вы можете ознакомиться на сайте рассылки [http://pterodactyl.l2p.net/php5/].


Subscribe.Ru
Поддержка подписчиков
Другие рассылки этой тематики
Другие рассылки этого автора
Подписан адрес:
Код этой рассылки: inet.webbuild.php5whatsnew
Архив рассылки
Отписаться Вебом Почтой
Вспомнить пароль

В избранное