using System;
using System.Collections.Generic;
using System.Collections.Concurrent;
namespace FactoryMethod
{
// ускладнимо архітектуру
// але забезпечимо повну незалежність класів у ієрархії
// створює продукт
public interface ICreator<out TReturnValue>
{
TReturnValue Create();
}
// фабрика
// реєструє, створює продукт
public interface IFactory<in TKey, in TRegValue, out TReturnValue>
where TRegValue : ICreator<TReturnValue>
{
void Registrate(TKey key, TRegValue value);
void UnRegistrate(TKey key);
TReturnValue MakeInstance(TKey key);
}
// ініціалізатор фабрики
// наповнює фабрику початковими значеннями
public interface IFactoryInitializer<in TFactory>
{
void Initialize(TFactory factory);
}
// узагальнений клас фабрики,
public class FactoryBase<TKey, TReturnValue> : IFactory<TKey, ICreator<TReturnValue>, TReturnValue>
{
// FIELDS
IDictionary<TKey, ICreator<TReturnValue>> factory;
// CONSTRUCTORS
public FactoryBase()
{
factory = new ConcurrentDictionary<TKey, ICreator<TReturnValue>>();
}
// METHODS
public TReturnValue MakeInstance(TKey key)
{
// checks
if (key == null) throw new ArgumentNullException(nameof(key));
if (!factory.ContainsKey(key)) throw new InvalidOperationException($"No such key '{key}'");
// make instance
return factory[key].Create();
}
public void Registrate(TKey key, ICreator<TReturnValue> creator)
{
// checking argument
// key
if (key == null) throw new ArgumentNullException(nameof(key));
if (factory.ContainsKey(key)) throw new InvalidOperationException($"Type by key '{key}' has been already registered");
// value
if (creator == null) throw new ArgumentNullException(nameof(creator));
Type creatorType = creator.GetType();
if (creatorType.IsInterface || creatorType.IsAbstract) throw new ArgumentException(nameof(creator));
// adding
factory.Add(key, creator);
}
public void UnRegistrate(TKey key)
{
// checking argument
// key
if (key == null) throw new ArgumentNullException(nameof(key));
if (!factory.ContainsKey(key)) throw new InvalidOperationException($"No such key '{key}'");
// removing
factory.Remove(key);
}
}
// SHAPES
// ієрархія класів
abstract class ShapeBase { }
class Square : ShapeBase { }
class Circle : ShapeBase { }
class Rectangle : ShapeBase { }
// SHAPE CREATORS
// для кожного класу в ієрархії варто написати
// клас відповідальний за створення
// (в C# не обов'язково, можна використати Activator)
class SquareCreator : ICreator<Square>
{
public Square Create()
{
// можливо створення за допомогою
// будь-якого, можливо твірного, шаблону проєктування
return new Square();
}
}
class DefaultCreator<T> : ICreator<T> where T: new()
{
public T Create()
{
return new T();
}
}
class CircleCreator : DefaultCreator<Circle> { }
// використовується при реєстрації
// DefaultCreator<Rectangle>
// зменшує кількість класів Creator'ів
// та деколи краще мати окремі класи Creator'ів для кожного об'єкта
// так при потребі змінити спосіб створення об'єкта, доведеться змінити лише відповідний метод Create
// а не створювати новий клас, та шукати усі місця в яких він реєструється
// FACTORY
// конкретна фабрика
class ShapeFactory : FactoryBase<string, ShapeBase>
{
public ShapeFactory(IFactoryInitializer<ShapeFactory> factoryInitializer)
{
factoryInitializer.Initialize(this);
}
}
// конкретний ініціалізатор
class DefaultShapeFactoryInitializer : IFactoryInitializer<ShapeFactory>
{
public void Initialize(ShapeFactory factory)
{
factory.Registrate(nameof(Square), new SquareCreator());
factory.Registrate(nameof(Circle), new CircleCreator());
factory.Registrate(nameof(Rectangle), new DefaultCreator<Rectangle>());
}
}
class Program
{
static void Main(string[] args)
{
ShapeFactory factory = new ShapeFactory(new DefaultShapeFactoryInitializer());
string[] shapeToCreate = { nameof(Square), nameof(Circle) };
foreach (string item in shapeToCreate)
{
ShapeBase shape = factory.MakeInstance(item);
Console.WriteLine(shape.ToString());
}
Console.ReadLine();
}
}
}