Шаблон Factory Method. Проблема

Опубликовал read-php в 03.02.2011 Категория: Генерация объектов ООП

В объектно-ориентированном проекте упор делается на абстрактном классе, а не на его реализации, т.е. мы работаем с обобщениями, а не с частностями. Шаблон Factory Method решает проблем создания экземпляров объектов, когда в коде используются абстрактные типы. И в чем же состоит решение? Пусть созданием экземпляров объектов занимаются специальные классы.

Проблема

Давайте рассмотрим проект личного дневника-календаря. В числе прочих, мы будем иметь дело с объектами Appointment (встреча). Наша бизнес-группа установила взаимоотношения с другой компанией, и мы должны передать им информацию о назначенной встрече с помощью формата, который называется BloggsCal. Но нас предупредили, что по прошествии времени могут понадобиться и другие форматы.
Рассуждая только на уровне интерфейса, мы сразу можем определить двух участников, Нам нужен кодировщик данных, который преобразовывает объекты типа Appointment во внутренний формат. Давайте назовем этот класс ApptEncoder. Нам нужен управляющий класс, который будет выполнять поиск кодировщика и, возможно, работать с ним, чтобы осуществлять коммуникации с третьей стороной. Назовем этот класс CommsManager. Используя терминологию шаблона, можем сказать, что CommsManager— это создатель, a ApptEncoder— продукт. Эта структура показана на рис. ниже.

Программирование PHP 02 Шаблон Factory Method. ПроблемаНо как нам получить реальный конкретный объект типа ApptEncoder? Мы можем потребовать, чтобы объект ApptEncoder передавался CommsManager. но это просто откладывает решение проблемы, а мы хотим решить ее сейчас. Давайте создадим экземпляр объекта BloggsApptEncoder непосредственно внутри класса CommsManager.

abstract class ApptEncoder { abstract function encode();

}

class BloggsApptEncoder extends ApptEncoder {

function encode() {

return «Данные о встрече закодированы в формате BloggsCal \п»;

class MegaApptEncoder extends ApptEncoder {

function encode() {

return «Данные о встрече закодированы в формате MegaCal \п»;

}

}

class CommsManager {

function getApptEncoder() {

return new BloggsApptEncoder();

}

}

Класс CommsManager отвечает за генерацию объектов BloggsApptEncoder. Теперь. когда нас попросят преобразовать нашу систему для работы с новым форматом MegaCal, мы просто добавим условный оператор в метод CommsManager: :getApptEncoder(). В конце концов, это стратегия, которую мы использовали в прошлом. Давайте создадим новую реализацию CommsManager, которая будет работать с обоими форматами: BloggsCal and MegaCal.

class CommsManager { const BLOOGS = 1; const MEGA – 2; private $mode = 1;

function _construct( $mode ) {

$this->mode = $mode;

}

function getApptEncoder() { switch ( $this->mode ) { case ( self::MEGA ):

return new MegaApptEncoder(); default:

return new BloggsApptEncoder();

}

}

}

$comms = new CommsManager( CommeManager::MEGA );

$apptEncoder = $comms->getApptEncoder();

print $apptEncoder->encode();

В качестве флажков, определяющих два режима, в которых может работать сценарий, мы используем константы класса MEGA и BLOGGS. В методе getApptEncoder () используется оператор switch, чтобы протестировать свойство $mode и создать экземпляр соответствующей реализации класса ApptEncoder.

Однако описанный подход не совсем удачен. Использование условных операторов иногда считается дурным тоном, но при создании объектов часто приходится их применять в определенный момент. Но не стоит слишком оптимистично относиться к тому, что в коде один за другим дублируются условные операторы. Класс CommsManager обеспечивает функции для передачи календарных данных. Предположим, что в используемом нами протоколе, нужно вывести данные в верхний и нижний колонтитул, чтобы очертить границы каждой встречи. Давайте расширим предыдущий пример и создадим метод getHeaderText ().

class CommsManager { const BLOGGS = 1; const MEGA = 2; private $mode = 1;

function _construct( $mode ) {

$this->mode = $mode;

}

function getHeaderText() { switch ( $this->modp ) { case ( self::MEGA ):

return «MegaCal верхний колонтитул\n»; default:

return «BloggsCal верхний колонтитул \n»;

}

}

function getApptEncoder() { switch ( $this->mode ) { case ( self::MEGA ):

return new MegaApptEncoder(); default:

return new BloggsApptEncoder();

}

}

}

Как видите, необходимость в поддержке выходных данных верхнего колонтитула заставила нас продублировать проверь типа протокола с помощью оператора switch. Но по мере добавления новых протоколов код делается громоздким, особенно если мы еще добавим метод get Foot erText ().

Итак, подытожим основные моменты проблемы.

  • До момента выполнения программы мы не знаем, какой вид объекта нам понадобится создать (BloggsApptEncoder или MegaApptEncoder).
  • Мы должны иметь возможность достаточно просто добавлять новые типы объектов (например, следующее требование бизнеса — поддержка протокола SyncML).
  • Каждый тип продукта связан с контекстом, который требует других специализированных операций (getHeaderText (), getFooterText ()).

Кроме того, нужно отметить, что мы используем условные операторы, и мы уже видели, что их можно заменить полиморфизмом. Шаблон Factory Method позволяет использовать наследование и полиморфизм, чтобы инкапсулировать создание конкретных продуктов. Другими словами, для каждого протокола создается свой подкласс типа CommsManager, в котором реализован свой метод getApptEncoder ().

P.S. Если есть необходимость найти нужный вам файл в интернете, то можно воспользоваться сайтом top4serials.com. Это уникальный file search engine с помощью которого можно найти интересующие вас файлы в сети.

Комментариев нет

Добавить комментарий