Фабричный шаблон с использованием отражения — регистрация класса с использованием статического ключа, связанного с каждым классом

Изучая некоторые шаблоны, я наткнулся на шаблон factory для создания экземпляров (неизвестных) классов. Это меня заинтересовало, поэтому я хочу создать программу, которую можно динамически улучшать. Итак, у меня есть некоторые базовые функции в одной сборке и фактические рабочие функции в разных сборках. Единственный намек, который я получаю от своего приложения, — это имя операции. Теперь я хочу создать нового работника, который зависит от операции. Поскольку я не знаю реальных реализаций рабочих, я выбрал такой шаблон.

Хорошо, подробнее:

У меня есть IWorker-интерфейс, который реализуют все мои процессы. Синглтон WorkerFactory позволяет мне создавать новые экземпляры любого работника, реализованного в любой сборке, по тому же пути, что и текущий. Для этого я добавил в свой интерфейс CreateWorker-метод, который принимает массив строк (которые были переданы как varargs в консольное приложение).

Прежде чем создать какой-либо экземпляр любого работника, я должен зарегистрировать любой класс на фабрике. К сожалению, в C# невозможно статически вставить такое поведение в любой статический контекст рабочих классов, потому что код в этом контексте выполняется только при доступе к классу каким-либо образом (будь то создание его экземпляра или доступ к любому из его статические члены, см. статические конструкторы). Поэтому я достигаю этого, отражая все типы, найденные во всех сборках, с путем к текущей выполняемой сборке. Теперь я могу проверить, реализует ли текущий тип интерфейс, и если да, то тип можно добавить в карту регистрации фабрики, содержащую имя операции (ключ) и работника (значение) как System.Type (см. Factory Pattern с использованием отражения). Проблема в том, как мне получить фактическое имя операции, на которую ссылается этот тип.

Я думаю, что есть два варианта для этого:

  1. Я добавляю статическое свойство к каждому рабочему классу, в котором хранится имя операции. Таким образом, я могу сослаться на операцию, отразив это свойство на полученном типе.
  2. Добавление нестатического свойства для имени и создание фиктивного экземпляра фактического рабочего класса при регистрации.

В то время как первый вариант имеет недостаток, заключающийся в том, что я не могу гарантировать, что все рабочие процессы действительно реализуют такое статическое свойство (следовательно, невозможно добавить статический член в интерфейс IWorker), второй имеет недостаток, заключающийся в том, что каждый класс должен иметь пустой конструктор для создания фиктивного экземпляра, из которого можно получить имя операции. Тем не менее, этот конструктор будет общедоступным, хотя и не будет содержать никакого кода инициализации.

Сказав, что весь этот вопрос касается лучших или, по крайней мере, лучших практик фабричного шаблона с использованием отражения. Я думаю, что предпочел бы первый вариант, но, возможно, я также пропустил какое-либо лучшее решение для этого подхода.

Следовательно, эта ветка уже довольно длинная. Я бы не хотел публиковать здесь какой-либо код, но дайте мне знать, если вам это нужно.


person HimBromBeere    schedule 03.07.2014    source источник


Ответы (1)


Вы можете использовать пользовательские атрибуты в своих рабочих классах:

[AttributeUsage(System.AttributeTargets.Class)]
public class WorkerAttribute : Attribute
{
    public WorkerAttribute (String operationType)
    {
       this.OperationType = operationType; 
    }

    public String OperationType {get; private set;}
}

public interface IWorker { }

[WorkerAttribute("Experienced")]
public class ExperiencedWorker: IWorker
{
}

Доступ к ним осуществляется так, как это описано здесь. Это будет что-то вроде:

       Type attributeType = typeof(WorkerAttribute);

        var myWorkerClassesPlusAttributes = from assembly in AppDomain.CurrentDomain.GetAssemblies()
                                            from type in assembly.GetTypes()
                                            let attributes = type.GetCustomAttributes(attributeType, true)
                                            where attributes.Any()
                                            select 
                                                new KeyValuePair<String, Type>(((WorkerAttribute)attributes.First()).OperationType, 
                                                                               type);

        Dictionary<String, Type> workers = new Dictionary<string, Type>();
        foreach (var item in myWorkerClassesPlusAttributes)
            workers.Add(item.Key, item.Value);

        IWorker worker = (IWorker)Activator.CreateInstance(workers["Experienced"]);

Он не самый лучший и не охватывает несколько атрибутов WorkerAttributes в классе, но его можно использовать в качестве основы для вашего решения.

person Eugene Podskal    schedule 03.07.2014
comment
Атрибут кажется мне довольно красивым, поэтому он разрешает статический доступ к операции. Последнее — это именно то, что я упомянул в варианте 2. Здесь мне понадобится конкретный (фиктивный) экземпляр фактического класса, чтобы получить его имя операции, не так ли? - person HimBromBeere; 03.07.2014
comment
А, я думал, вы полагаетесь на создание экземпляра рабочего. Однако спасибо за ссылку. - person HimBromBeere; 03.07.2014
comment
Мне уже понравилось ваше решение, прежде чем вы добавили все LINQ-материалы. Однако отличная работа - person HimBromBeere; 03.07.2014