Этот паттерн активно используется в связке с другим паттерном, Abstract Factory (Абстрактная фабрика), рассмотренным в части 1 нашего цикла статей («Мир ПК», №10/09, с.46).

Вот наиболее популярные случаи, в которых имеет смысл применять обсуждаемый здесь шаблон:

  • класс, создающий подклассы, заранее не знает, какими они будут;
  • класс был спроектирован так, что создаваемые им объекты специфицируются подклассами;
  • класс делегирует свои обязанности одному из вспомогательных подклассов, после чего планируется локализовать знание о том, какой класс принимает эти обязанности на себя.

Для того чтобы приведенные ситуации стали понятнее, рассмотрим абстрактную структуру паттерна, а заодно объявим имена классов из примера, приведенного ниже. Итак, в нашем паттерне представлены четыре участника, взаимодействие которых показано на схеме:

Product — собственно продукт. Предназначен для определения интерфейса объектов, создаваемых фабричным методом; в нашем примере он будет носить имя Employe;

ConcreteProduct — конкретный продукт, участвующий в схеме и отвечающий за реализацию интерфейса Product. В нашем случае предполагается, что Manager и Programmer наследуются от Employe. В примере будет продемонстрирована работа с конкретными продуктами ManagerEmploye и ProgrammerEmploye;

Creator — создатель, и его название говорит само за себя. Данный объект предназначен для объявления фабричного метода, возвращающего объект типа Product. В некоторых случаях Creator хранит определение реализации по умолчанию объекта ConcreteProduct. Кроме того, Creator может вызывать фабричный метод, чтобы создать объект Product. В примере это название меняться не будет;

ConcreteCreator — конкретный создатель. Здесь все очевидно: конкретная реализация создателя занимается тем, что возвращает ConcreteProduct. В нашем примере две конкретные реализации создателя — ManagerCreator и ProgrammerCreator.

Создатель доверяет своим подклассам реализацию подходящего конкретного продукта. В этом и заключается суть Factory Method. Но прежде чем разбирать текст программы, отметим плюсы и минусы данного паттерна.

Самый очевидный недостаток Factory Method — необходимость создавать наследника Creator всегда, когда планируется получить новый тип продукта (т.е. новый ConcreteProduct). И этого, увы, не избежать. Но подобная проблема присутствует во многих порождающих шаблонах. К достоинствам же следует отнести возможность создавать объекты более универсально, не ориентируясь на конкретные классы и оперируя общим интерфейсом.

А теперь разберемся с текстом программы. Для начала нужно объявить так называемый абстрактный класс Product и его конкретные реализации (листинг 1).

Здесь просто объявлен абстрактный класс, предоставляющий интерфейс для своих наследников. В данном случае мы рассматриваем некий абстрактный класс для хранения информации о сотрудниках организации. Конкретизируем его в двух подклассах — ManagerEmploye и ProgrammerEmploye (листинг 2).

Итак, выше просто описана логика двух абстрактных методов. Первый (Status) из них позволяет узнать, что за работник представлен нам, а второй  (Salary) — его заработную плату (листинг 3).

С классом Programmer все происходит аналогично. Теперь обсудим абстрактный создатель и его конкретные реализации. При этом можно увидеть Factory Method в деле (листинг 4).

Вот так просто выглядит создатель (листинг 5), содержащий всего один метод, предназначенный для возвращения объекта Employe (и его наследников).

В первой конкретной реализации Creator вместо Employe возвращается его наследник ManagerEmploye (листинг 6).

Во второй конкретной реализации Creator (листинг 7) на место Employe становится его наследник ProgrammerEmploye.

В общем, все очевидно. А сейчас перейдем к методу Main, используемому для работы с Factory Method:

На этом шаге объявляется массив, в котором будут содержаться объекты ManagerCreator и ProgrammerCreator. Работать с ними нужно через абстрактный интерфейс. После заполнения массива переходим к его считыванию. В цикле оперируем всеми объектами, наследниками Employe, возвращая их с помощью фабричного метода — FactoryMethod(). После этого нужно вызвать методы объектов Status и Salary, а затем выяснить с помощью GetType(), к какому типу относится каждый из объектов. А в самой последней строке программы следует просто ожидать, какие действия предпримет пользователь. Если он нажмет любую клавишу, то получившееся консольное приложение будет завершено.

Итак, мы обсудили уже четвертый порождающий паттерн. По своей сути и структуре он достаточно прост, хотя и более громоздок, чем, например, Singleton, рассмотренный в части 3. Попробуйте сами написать Factory Method со своими объектами, и вы убедитесь, насколько легко работать с этим шаблоном.


Листинги 1-5