Реализация паттерна Abstract Factory на языке С# Дроздова С. Е.1, Кудрина Е.В.2, Кузнецов А.В.3 1 drozdovase@gmail.com, 2kudrinaev@mail.ru, 3alexander_kuznetsov1@epam.com 1,2 ФГБОУ ВПО «Саратовский государственный университет имени Н.Г.Чернышевского», Саратов, Россия; 3EPAM Systems, Саратов, Россия В статье рассматривается роль шаблонов проектирования в инженерии программного обеспечения. Предложена реализация одного из порождающих паттернов – Abstract Factory – на языке С#. Ключевые слова: паттерны, язык программирования С#, инженерия программного обеспечения Шаблоном проектирования или паттерном (design pattern) называется повторимая архитектурная конструкция, адаптированная для решения общей задачи проектирования в конкретном контексте [1]. В наше время паттерны играют важную роль в инженерии программного обеспечения. Повторно используемое программное обеспечение (ПО) часто приходится реорганизовывать. В таком случае паттерны проектирования помогают решить, как именно реорганизовать проект, и могут уменьшить вероятность реорганизации в будущем. [2, с.335] Существуют несколько типов паттернов проектирования, каждый из которых предназначен для решения своего круга задач: Порождающие паттерны – предназначены для создания новых объектов в системе; Структурные паттерны – решают задачи компоновки системы на основе классов и объектов; Паттерны поведения – предназначены для распределения обязанностей между объектами в системе. В данной статье мы рассмотрим реализацию одного из порождающих паттернов – Abstract Factory (абстрактная фабрика) – на языке С#. Паттерн Abstract Factory используется, если система должна оставаться независимой от процесса создания новых объектов и их типов, и необходимо исключить возможность одновременного использования объектов из разных семейств в одном контексте. Например, приложение может использовать различные системы управления базами данных (СУБД), например, MySQL, Oracle и др. Данный паттерн позволит абстрагироваться от конкретной СУБД, так как всё взаимодействие с базой данных будет инкапсулировано внутри продуктов конкретных фабрик. Структура паттерна Abstract Factory представлена на рисунке 1. Рис. 1. Паттерн Abstract Factory [2, с.95] AbstractFactory — абстрактный класс или интерфейс, который определяет механизмы создания абстрактных продуктов. ConcreteFactory — класс, реализующий методы получения одного или нескольких конкретных продуктов. AbstractProduct — абстрактный класс или интерфейс, который описывает общий интерфейс для продукта. ConcreteProduct — класс, определяющий конкретный продукт, создаваемый соответствующей фабрикой. Client — фрагмент кода, дающий команды на получение конкретного семейства продуктов, пользуясь только известными интерфейсами классов AbstractFactory и AbstractProduct. Рассмотрим игру про военные действия во время Второй мировой войны. Игрок может выбрать одно из двух противоборствующих государств — Германию или СССР. У каждой страны есть по два типа техники — танк и самолет. Использование паттерна Abstract factory позволит максимально абстрагировать действия игрока от страны, за которою он играет. С точки зрения управления, нет никакой разницы между танками или самолетами Германии и СССР. Тогда, чтобы обеспечить независимость управления, можно извлечь их общие методы в отдельные интерфейсы или абстрактные классы, что и приведено на рисунке 2. Рис. 2. Абстрактные классы Tank и Aircraft и их потомки В приведенном ниже коде описан класс Tank и его потомки: T_34, E_25. Класс Aircraft и его потомки (Il_2, MesserschmittBf110) имеют аналогичный код. public abstract class Tank { public abstract void RotateTurret(); } public class T_34 : Tank { public T_34() { Console.WriteLine("T-34"); } public override void RotateTurret() { Console.WriteLine("Башня T-34 повернута"); } } public class Е_25 : Tank { public Е_25() { Console.WriteLine("Е-25"); } public override void RotateTurret() { Console.WriteLine("Башня Е-25 повернута"); } } Теперь действия игрока не зависят от того, за какую страну он играет: команда RotateTurret отдается не какому-то конкретному танку, а абстрактному классу: tank.RotateTurret(). Введем в конструкцию еще одну абстракцию. Пусть созданием техники будет заниматься отдельный класс. Класс USSREngineryFactory позволит создать только советскую военную технику, а GermanEngineryFactory – немецкую. Методы, возвращающие определенного вида технику соответствующей страны, можно извлечь в абстрактный базовый класс или интерфейс. На рисунке 3 представлена иерархия фабрик. Рис. 3. Фабрики военной техники В представленном ниже коде описан класс EngineryFactory и его потомки: USSREngineryFactory и GermanEngineryFactory. public abstract class EngineryFactory { public abstract Tank CreateTank(); public abstract Aircraft CreateAircraft(); } public class USSREngineryFactory : EngineryFactory { public override Tank CreateTank() { return new T_34(); } public override Aircraft CreateAircraft() { return new Il_2(); } } public class GermanEngineryFactory : EngineryFactory { public override Tank CreateTank() { return new Е_25(); } public override Aircraft CreateAircraft() { return new MesserschmittBf110(); } } Теперь общая схема управления будет иметь вид, показанный на рисунке 4. Рис. 4. Общая схема управления При запуске игры управляющий класс (Controller) играет за одну из двух стран, но класс, инкапсулирующий в себе действия игрока, ничего не знает о том, какую именно страну он выбрал. public class Controller { EngineryFactory enginery; // Фабрика, отвечающая за создание техники. public Controller(EngineryFactory enginery) { this.enginery = enginery; } public void PlayGame() { // Создание техники не зависит от страны. Tank tank = enginery.CreateTank(); Aircraft aircraft = enginery.CreateAircraft(); // Вызов команд для техники не зависит от страны. tank.RotateTurret(); aircraft.RetractFlaps(); } } // Создание классов-контроллеров, работающих с соответствующими фабриками Controller ussr_controller = new Controller(new USSREngineryFactory()); Controller germany_controller = new Controller(new GermanEngineryFactory()); // Игра за СССР ussr_controller.PlayGame(); // Игра за Германию germany_controller.PlayGame(); Если необходимо добавить еще одну страну, то это сведется к написанию еще одной фабрики и соответствующих классов. Тогда, чтобы начать играть, например, за США, необходимо добавить пару строк: Controller usa_controller = new Controller(new USAEngineryFactory()); usa_controller.PlayGame(); Использование паттернов на практике имеет как преимущества, так и недостатки. Преимущества паттерна Abstract Factory: скрывает процесс порождения объектов и делает систему независимой от типов создаваемых объектов; позволяет быстро добавить новые группы взаимосвязанных объектов (новую страну и ее технику). Недостатки паттерна Abstract Factory: трудно добавлять новые типы создаваемых объектов (новая техника) или изменять существующие (добавление технике новой команды), так как интерфейс базового класса абстрактной фабрики фиксирован. В таком случае придется изменять все классы соответствующей группы. Список литературы [1] Шаблон проектирования [Электронный ресурс]. URL: http://ru.wikipedia.org/wiki/Design_pattern (дата обращения: 20.03.2014). [2] Приемы объектно-ориентированного проектирования. Паттерны проектирования.// Эрих Гамма[и. др.]. – СПб: Питер, 2001. – 366 с.