Заводской шаблон со статической регистрацией

У меня возникла проблема при попытке зарегистрировать мои типы с помощью их статических конструкторов со следующей фабрикой:

 public class Factory<T>
{
    public static Factory<T> Instance { get { return _instance; } }

    private static Factory<T> _instance = new Factory<T>();
    private Factory() { }
    static Factory() { }

    static Dictionary<string, Type> _registeredType = new Dictionary<string, Type>();

    public void Register(string id, T obj)
    {
        if (obj.GetType().IsAbstract || obj.GetType().IsInterface)
            throw new ArgumentException("Cannot create instance of interface or abstract class");

        _registeredType.Add(id, obj.GetType());
    }

    public T Create(string id, params object[] parameters)
    {
        Type type;

        if(!_registeredType.TryGetValue(id, out type))
            throw new UnsupportedShapeException(id);

        return (T)Activator.CreateInstance(type, parameters);
    }
} 

Затем, если я использую статический конструктор для регистрации, он не работает:

    public interface IShape
{
    string print { get; }
}

public class Circle : IShape
{
    static Circle()
    {
        Factory<IShape>.Instance.Register("Circle", new Circle());
    }

    public string print
    {
        get
        {
            return "Circle";
        }
    }
}

Где я ошибаюсь? Фабрика, кажется, настроена нормально, но я просто не могу заставить работать ctor. Ваше здоровье.


person edwin the duck    schedule 11.01.2016    source источник
comment
Статический конструктор выполняется при первом доступе кода к классу, вы где-то обращаетесь к этому классу?   -  person Gusman    schedule 11.01.2016
comment
Пожалуйста, уточните, как это не работает.   -  person Mike Zboray    schedule 11.01.2016
comment
Извините, я должен был быть яснее, я пытаюсь реализовать это: oodesign.com/factory- шаблон.html   -  person edwin the duck    schedule 11.01.2016
comment
В приведенном выше коде ваша фабрика должна быть объявлена ​​статическим членом круга, а конструктор удален.   -  person Ross Bush    schedule 11.01.2016
comment
Я должен был быть более конкретным. Если я возьму этот код и скомпилирую его (есть еще пара ошибок компилятора, которые нужно исправить), он ничего не сделает. Таким образом, у вас должно быть 1) некоторый дополнительный код, который не показан, 2) некоторые ожидания относительно того, каким должен быть вывод этого кода, и 3) фактический вывод (который отличается). Пожалуйста, добавьте эту информацию.   -  person Mike Zboray    schedule 11.01.2016
comment
На самом деле я нигде не ссылался на конкретные классы ... вау, мой плохой! Ваше здоровье.   -  person edwin the duck    schedule 12.01.2016


Ответы (2)


Это не ответ, а совет. Во-первых, когда вы используете универсальный класс, CLR фактически создает класс для каждой реализации. Эти классы будут иметь разные статические переменные, и вы не сможете использовать одну фабрику для всех классов. Хорошей новостью является то, что вы можете использовать универсальные методы вместо универсального класса. И вам даже не нужно создавать экземпляр объекта T:

public class Factory
{
    public static Factory Instance { get { return _instance; } }

    private static Factory _instance = new Factory();
    private Factory() { }

    static Dictionary<string, Type> _registeredType = new Dictionary<string, Type>();

    public void Register<T>(string id)
    {
        var type = typeof(T);
        if (type.IsAbstract || type.IsInterface)
            throw new ArgumentException("Cannot create instance of interface or abstract class");

        _registeredType.Add(id, type);
    }

    public T Create<T>(string id, params object[] parameters)
    {
        Type type;

        if(!_registeredType.TryGetValue(id, out type))
            throw new UnsupportedShapeException(id);

        return (T) Activator.CreateInstance(type, parameters);
    }
} 

Теперь вы можете использовать Factory для регистрации и разрешения объектов:

Factory.Instance.Register<Circle>("Circle");
var firstCircle = Factory.Instance.Create<Circle>("Circle");
var secondCircle = Factory.Instance.Create<IShape>("Circle");
person Vadim Martynov    schedule 11.01.2016
comment
Это хорошо, но, учитывая шаблон OP, я думаю, что Create должен возвращать object вместо T и не быть универсальным, чтобы строка id была ясной как единственное определение типа вывода. Это также позволяет избежать фабрики, которая является местом любого неправильного приведения, когда типы несовместимы и возникает исключение (общий - это просто подсказка приведения). Это приводит к коду, который (для меня) намного понятнее, что происходит: var firstCircle = (Circle) Factory.Instance.Create("Circle"); var secondCircle = (IShape) Factory.Instance.Create("Circle"); - person ErikE; 12.01.2016
comment
@ErikE на самом деле мы можем сравнивать типы из словаря и общего параметра в методе Create, и если тип не реализует/наследует T, выдается собственное исключение, которое покажет реальную проблему. Но это то же самое, но немного строго типизированное использование общих параметров и результат приведения внутри фабрики. ИМХО. А строка - это просто строка. Есть ли причина в сценарии автора регистрировать типы по строковому идентификатору или он может просто использовать универсальную оболочку для Activator.CreateInstance? - person Vadim Martynov; 12.01.2016
comment
@Вадим, мне нравится! Для сценария, который я использовал, мне требуется идентификатор, но я думаю, что ваша реализация может быть очень эффективной без него, поэтому, как вы сказали, общий тип будет единственным идентификатором, который ему нужен. Итак.. Factory.Instance.Create‹Circle›(); - person edwin the duck; 12.01.2016
comment
В простейшем сценарии с Factory.Instance.Create<Circle>(); для конкретных классов это может быть просто оболочка: public T Create<T>(params object[] parameters) { return (T) Activator.CreateInstance(typeof(T), parameters); } - person Vadim Martynov; 12.01.2016
comment
Если он создает типы по строке, то должна работать только строка. Необходимость также указать тип побеждает весь сценарий. - person ErikE; 12.01.2016

Я не уверен на 100%, что знаю, к чему вы стремитесь, однако, вероятно, было бы лучше создать классы controller'esqe, содержащие экземпляр вашей фабрики. Однако внедрение конструктора не будет работать со статическими классами или потомками.

public static class StaticFactoryClassController
{
    private static readonly IStaticFactoryService service=AppServiceFactory.Instance.Create<IStaticFactoryService>();

    public static void DoSomething() 
    {
        Service srv = new StaticFactoryClassService(service);
        srv.DoSomething(); 
    }
}

И с этим вы могли бы создать класс обслуживания...

public class StaticFactoryClassService
{
    private readonly IStaticFactoryService service;

    public StaticFactoryClassService(IStaticFactoryService service)
    {
        this.service = service;
    }

    public void DoSomething()
    {
       this.service.DoSomething();
    }
}

И, наконец, ваш интерфейс привязки...

public interface IStaticFactoryService
{
    DoSomething();
}  
person Ross Bush    schedule 11.01.2016