Паттерн Factory
Часть раздела Объектно-ориентированное программирование путешествия по C# на Coddy — урок 53 из 70.
Паттерн «Фабрика» (Factory pattern) — это порождающий паттерн, который делегирует создание объектов отдельному методу или классу. Вместо того чтобы использовать new напрямую в коде, вы просите фабрику создать объекты за вас. Это централизует логику создания и делает ваш код более гибким.
Рассмотрим сценарий, в котором вам необходимо создавать различные типы документов. Без фабрики ваш код становится тесно связанным с конкретными классами:
// Без Фабрики — разрозненная логика создания
IDocument doc;
if (type == "pdf")
doc = new PdfDocument();
else if (type == "word")
doc = new WordDocument();Фабрика инкапсулирует этот процесс принятия решений в одном месте:
public interface IDocument
{
void Open();
}
public class PdfDocument : IDocument
{
public void Open() => Console.WriteLine("Opening PDF");
}
public class WordDocument : IDocument
{
public void Open() => Console.WriteLine("Opening Word");
}
public class DocumentFactory
{
public IDocument Create(string type)
{
return type switch
{
"pdf" => new PdfDocument(),
"word" => new WordDocument(),
_ => throw new ArgumentException("Unknown type")
};
}
}Теперь клиентский код просто запрашивает то, что ему нужно, не зная конкретных классов:
var factory = new DocumentFactory();
IDocument doc = factory.Create("pdf");
doc.Open(); // Открытие PDFПаттерн «Фабрика» проявляет себя наилучшим образом, когда вам нужно добавить новые типы позже. Добавление SpreadsheetDocument требует изменений только в фабрике, а не во всех местах, где создаются документы. Такое разделение делает ваш код более простым в поддержке и тестировании.
Задание
ЛегкоДавайте создадим систему уведомлений, используя паттерн «Фабрика» (Factory). Вместо того чтобы создавать различные типы уведомлений напрямую в коде, вы централизуете логику создания в классе фабрики, который решает, какое уведомление создать, на основе простого строкового идентификатора.
Вы организуете свой код в трех файлах:
Notification.cs: Определите интерфейсINotificationв пространстве именNotificationsс единственным методомSend(string message), который возвращает строку. Затем создайте три класса, реализующих этот интерфейс:EmailNotification— возвращает"Email: {message}"SmsNotification— возвращает"SMS: {message}"PushNotification— возвращает"Push: {message}"
NotificationFactory.cs: Создайте классNotificationFactoryв том же пространстве имен с методомCreate(string type), который возвращает соответствующийINotificationна основе строки типа:"email"возвращаетEmailNotification"sms"возвращаетSmsNotification"push"возвращаетPushNotification
EmailNotificationпо умолчанию.Program.cs: Объедините все вместе, создав экземпляр фабрики и используя его для создания уведомлений на основе входных данных. Фабрика берет на себя принятие решений о том, какой конкретный класс инстанцировать.
Вы получите два входных значения:
- Тип уведомления (например,
sms) - Сообщение для отправки (например,
Your order has shipped)
Используйте свою фабрику для создания соответствующего типа уведомления, затем вызовите его метод Send с сообщением и выведите результат.
Например, если входные данные — push и Meeting in 5 minutes, результат должен быть:
Push: Meeting in 5 minutesОбратите внимание, что ваш основной код никогда не использует new EmailNotification() или подобные конструкции — он просто запрашивает у фабрики то, что ему нужно. Если позже вы добавите SlackNotification, вам нужно будет обновить только фабрику, а не каждое место, где создаются уведомления!
Шпаргалка
Паттерн Фабрика (Factory pattern) — это порождающий паттерн проектирования, который делегирует создание объектов отдельному методу или классу, централизуя логику создания и делая код более гибким.
Без фабрики создание объектов разбросано по коду и имеет сильную связанность:
// Без Фабрики — разрозненная логика создания
IDocument doc;
if (type == "pdf")
doc = new PdfDocument();
else if (type == "word")
doc = new WordDocument();С фабрикой логика создания инкапсулирована в одном месте:
public interface IDocument
{
void Open();
}
public class PdfDocument : IDocument
{
public void Open() => Console.WriteLine("Opening PDF");
}
public class WordDocument : IDocument
{
public void Open() => Console.WriteLine("Opening Word");
}
public class DocumentFactory
{
public IDocument Create(string type)
{
return type switch
{
"pdf" => new PdfDocument(),
"word" => new WordDocument(),
_ => throw new ArgumentException("Unknown type")
};
}
}Клиентский код запрашивает объекты, не зная о конкретных классах:
var factory = new DocumentFactory();
IDocument doc = factory.Create("pdf");
doc.Open(); // Opening PDFПаттерн Фабрика упрощает добавление новых типов — изменения изолированы в фабрике, а не разбросаны по всей кодовой базе.
Попробуйте сами
using System;
using Notifications;
class Program
{
public static void Main(string[] args)
{
// Чтение входных данных
string type = Console.ReadLine();
string message = Console.ReadLine();
// TODO: Создайте экземпляр NotificationFactory
// TODO: Используйте фабрику для создания соответствующего уведомления
// TODO: Вызовите метод Send и выведите результат
}
}
В этом уроке есть небольшой тест. Начните урок, чтобы ответить на вопросы и сохранить прогресс.
Все уроки раздела Объектно-ориентированное программирование
1Основы ООП
Внешние файлыПространства имен и директивыВведение в классы и объектыКлючевое слово 'this'Методы и параметрыПоля и свойстваКонструкторыИнициализаторы объектовИтоги — Простой калькулятор4Наследование
Основы синтаксиса наследования (:)Ключевое слово 'base'Ключевые слова Virtual и OverrideЗапечатанные классыБазовый класс 'object'Итоги — Иерархия сотрудников7Продвинутые возможности
Перегрузка операторовИндексаторы (this[])Переопределение ToString()Методы расширенияПовторение — Пользовательский список10Паттерны проектирования. Часть 1
Введение в паттерны проектированияПотокобезопасный SingletonПаттерн FactoryПаттерн Observer (События)Паттерн Strategy13Финальные испытания
Платформа для онлайн-обученияКомпонент игрового персонажаСервис аренды транспорта2Свойства и статические члены
Автоматически реализуемые свойстваСвойства только для чтения и записиСтатические поля и методыСтатические классыЧлены в виде выражений5Полиморфизм и интерфейсы
Полиморфизм: компиляция vs выполнениеИнтерфейс vs абстрактный классМножественные интерфейсыЯвные интерфейсыUpcasting и DowncastingИтоги: Калькулятор фигур8Продвинутые концепции ООП
Композиция вместо наследованияGenerics (классы и методы)Делегаты и событияАтрибуты и рефлексияIDisposable и оператор usingОсновы Dependency Injection11Паттерны проектирования. Часть 2
Паттерн КомандаПаттерн АдаптерПаттерн ДекораторПаттерн Шаблонный методПаттерн СостояниеПаттерн Компоновщик3Архитектура классов
Экземплярные и статические данныеКлючевые слова 'readonly' и 'const'Опорные поляПовторение — Менеджер банковских счетов6Инкапсуляция
Модификаторы доступаСвойства для инкапсуляцииРеализация скрытия данныхПаттерны неизменяемостиИтоги — Записи студентов9Переменное количество аргументов
Ключевое слово 'params'Необязательные параметрыИменованные аргументыПерегрузка методов