Шаблон дизайна фабрики ограниченного класса
Есть ли элегантный (или какой-либо) способ добиться следующего в C#?
- Давай класс
ItemBase
(далее выводится вItem1
,Item2
...), который не допускает непосредственное создание экземпляров (непубличное построение) - чтобы пользователь не мог создать "неотслеживаемый" экземплярItem*
, - Давайте иметь нестатический класс
Manager
чьи экземпляры (допускается несколько экземпляров) могут создавать и предоставлять экземплярыItem*
(потому что они отслеживают произведенные экземпляры и выполняют дополнительную работу). - Давайте иметь необязательное требование:
Manager
инстанции хотели бы манипулировать непубличными членами управляемыхItem
экземпляры (похожие какManager
будетfriend
изItem*
). - Было бы хорошо, если
Manager
не вынужден быть производнымItem*
, - Было бы хорошо, если бы было как можно меньше отражений.
Заметки:
Если возможно, пожалуйста, рассматривайте это как вопрос, возникающий в процессе размышления о том, как реализовать решение конкретной проблемы наилучшим и элегантным способом Я хотел бы, чтобы это было общим, и нет, у меня нет источников, и да, я уже попробовал некоторые варианты, но ни один из них не удовлетворил мои потребности. Спасибо.
Насколько я знаю, нет приемлемого
friend
альтернатива (любой изinternal
а такжеInternalsVisibleToAttribute
кажется, хорошо), так чтоItemBase
просто предоставляет "специальные" (но общедоступные) методы модификации, и пользователь должен знать, что эти методы не для него:o(Мне нравится это решение, но я не могу придумать, как разрешить множественное
Manager
случаи использования его.
2 ответа
Хорошо, сдаюсь. Если это поможет полностью понять цель, есть менее плохое решение, которое я (в настоящее время) нашел. Передача функций создания осуществляется через статические конструкторы (которые недоступны пользователям), к сожалению, уродливым является их вызов...
Есть идеи как сделать лучше?
Определения предметов:
namespace SpecialFactory
{
public enum ItemType
{
Item1,
Item2,
// ... Anyone deriving the Item* should add an item here
}
public abstract class ItemBase
{
public abstract ItemType Id {get;}
public static void RegisterAllCreators()
{
// Force static constructors invocation
var it = Item1.ClassId | Item2.ClassId; // Anyone deriving the Item* should ensure invocation of Manager.RegisterCreator
}
}
public class Item1 : ItemBase
{
static Item1()
{
Manager.RegisterCreator(ItemType.Item1, () => new Item1());
}
protected Item1()
{
}
public static ItemType ClassId => ItemType.Item1;
public override ItemType Id => ClassId;
}
public class Item2 : ItemBase
{
static Item2()
{
Manager.RegisterCreator(ItemType.Item2, () => new Item2());
}
protected Item2()
{
}
public static ItemType ClassId => ItemType.Item2;
public override ItemType Id => ClassId;
}
}
Менеджер:
namespace SpecialFactory
{
public class Manager
{
static Manager()
{
ItemBase.RegisterAllCreators();
}
protected static Dictionary<ItemType, Func<ItemBase>> creators = new Dictionary<ItemType, Func<ItemBase>>();
protected readonly List<ItemBase> managedItems = new List<ItemBase>();
protected ItemBase CreateItem(ItemType type)
{
ItemBase item = null;
if (creators.ContainsKey(type))
{
if ((item = creators[type]()) != null)
managedItems.Add(item);
}
return item;
}
public static void RegisterCreator(ItemType type, Func<ItemBase> creator)
{
if (!creators.ContainsKey(type))
creators[type] = creator;
}
public Manager()
{
}
public ItemBase Test(ItemType type)
{
// var notAllowed = new Item1();
var allowed = CreateItem(type);
return allowed;
}
}
}
Тест:
namespace SpecialFactory
{
class Program
{
static void Main(string[] args)
{
var m1 = new Manager();
var m2 = new Manager();
var i1 = m1.Test(ItemType.Item1);
var i2 = m2.Test(ItemType.Item2);
}
}
}
Я думаю, что это может ответить на вашу проблему:
public class ItemBase
{
protected ItemBase()
{
}
public void PublicMethod() { }
public int PublicProperty { get; set; }
}
public class Factory
{
private class PrivateItemBase : ItemBase
{
public void PrivateMethod() { }
public int PrivateProperty { get; set; }
}
public Factory(int id)
{
}
public IEnumerable<ItemBase> Items { get; private set; }
public ItemBase CreateItem()
{
PrivateItemBase rValue = new PrivateItemBase();
rValue.PrivateMethod();
rValue.PrivateProperty = 4;
return rValue;
}
}