Порождающие паттерны: «Фабричный метод» (Factory Method)
Назначение: определяет интерфейс для создания объекта, но оставляет подклассам решение о том, какой класс инстанцировать. Фабричный метод позволяет классу делегировать инстанцирование подклассам.
Когда следует использовать фабричный метод
- Когда заранее неизвестно, объекты каких типов необходимо создавать;
- Когда система должна быть независимой от процесса создания новых объектов и расширяемой: в нее можно легко вводить новые классы, объекты которых система должна создавать;
- Когда создание новых объектов необходимо делегировать из базового класса классам наследникам;
Схема примера использования паттерна "Фабричный метод" из реальной жизни:
UML схема паттерна фабричный метод
Реализация шаблона "фабричный метод" на C#
using System;
namespace DoFactory.GangOfFour.Factory.Structural
{
class MainApp
{
static void Main()
{
// An array of creators
Creator[] creators = new Creator[2];
creators[0] = new ConcreteCreatorA();
creators[1] = new ConcreteCreatorB();
// Iterate over creators and create products
foreach (Creator creator in creators)
{
Product product = creator.FactoryMethod();
Console.WriteLine("Created {0}",
product.GetType().Name);
}
// Wait for user
Console.ReadKey();
}
}
abstract class Product
{
}
class ConcreteProductA : Product
{
}
class ConcreteProductB : Product
{
}
abstract class Creator
{
public abstract Product FactoryMethod();
}
class ConcreteCreatorA : Creator
{
public override Product FactoryMethod()
{
return new ConcreteProductA();
}
}
class ConcreteCreatorB : Creator
{
public override Product FactoryMethod()
{
return new ConcreteProductB();
}
}
}
Output
Created ConcreteProductA
Created ConcreteProductB
Обсуждение паттерна «Фабричный метод»
У каждой реализации фабричного метода есть свои особенности
- Классический фабричный метод является частным случаем шаблонного метода. Это значит, что фабричный метод привязан к текущей иерархии типов и не может быть использован повторно в другом контексте.
- Полиморфный фабричный метод является стратегией создания экземпляров некоторого семейства типов, что позволяет использовать одну фабрику в разных контекстах. Тип создаваемого объекта определяется типом фабрики и обычно не зависит от аргументов фабричного метода.
- Статический фабричный метод является самой простой формой фабричного метода. Статический метод создания позволяет обойти ограничения конструк- торов. Например, тип создаваемого объекта может зависеть от аргументов ме- тода, экземпляр может возвращаться из кэша, а не создаваться заново или же фабричный метод может быть асинхронным.
Использование Func в качестве фабрики
В некоторых случаях Func может использоваться в качестве полноценной фабрики и передаваться классу извне его клиентами. Данный вариант фабрики является допустимым во внутреннем (internal) коде и только для функций с небольшим количеством аргументов. Понять, что делает Func, довольно просто, но разобраться в назначении Func<int, string, int, ValidationResult> factory без контекста будет практически невозможно.Читаемость кода очень важна, поэтому именованная фабрика является предпочтительным вариантом.
Конструктор vs. фабричный метод
В объектно-ориентированных языках программирования конструктор отвечает за корректную инициализацию создаваемого объекта. В большинстве случаев они прекрасно справляются со своей задачей, но иногда лучше воспользоваться статическим фабричным методом.
Именованные конструкторы.
В языке C# имя конструктора совпадает с именем класса, что делает невозможным использование двух конструкторов с одним набором и типом параметров. Хорошим примером такого ограничения является структура Timespan, которая представляет собой интервал времени. Очень удобно создавать интервал времени по количеству секунд, минут, часов и дней, но сделать несколько конструкторов, каждый из которых принимает один параметр типа double, невозможно. Для этого структура Timespan содержит набор фабричных методов (пример ниже)
public struct Timespan
{
public Timespan(double ticks) { ... }
public static Timespan FromMilliseoncds(double value) {...}
public static Timespan FromSeconds(double value) {...}
public static Timespan FromMinutes(double value) {...}
}
Тяжеловесный процесс создания
Конструктор отвечает за корректную инициализацию объекта, после которой объект должен быть готов для использования своими клиентами. Обычно логика инициализации относительно простая и должна выполняться конструктором, но слишком тяжеловесную логику лучше вынести из конструктора в статический фабричный метод
Примеры в .NET Framework
В .NET Framework применяется огромное количество фабрик.
- Классический фабричный метод: Stream.CreateWaitHandle, SecurityAttribute.CreatePermission, ChannelFactory.CreateChannel, XmlNode.CreateNavigator.
- Полиморфная фабрика: IControllerFactory в ASP.NET MVC, IHttpHandlerFactory в ASP.NET, ServiceHostFactory и IChannelFactory в WCF, IQueryProvider в LINQ.
- Неполиморфная фабрика: TaskFactory в TPL.
- Обобщенная статическая фабрика: Activator.CreateInstance, Array.CreateInstance, StringComparer.Create.
- Сокрытие наследников: RandomNumberGenerator.Create, WebRequest.Create, BufferManager.CreateBufferManager, Message.CreateMessage, MessageFault.CreateFault
- Фасадные фабричные методы: File.Create, File.CreateText.
- Именованные конструкторы: Timespan.FromSecond, Timespan.FromMilliseconds, GCHandle.FromIntPtr, Color.FromArgb.