Фабричен модел, използващ отражение - регистрация на клас, използвайки статичен ключ, свързан с всеки клас

Докато изучавах някои модели, попаднах на фабричния модел за създаване на екземпляри на (неизвестни) класове. Това ме накара да се заинтересувам, затова искам да създам програма, която може да бъде динамично подобрена. Така че имам някои основни функции в рамките на една сглобка и действителните работници в различни сглобки. Единственият намек, който получавам от моето приложение, е име на операция. Сега искам да създам нов работник, който разчита на операцията. Тъй като не знам действителните реализации на работниците, избрах този вид модел.

Добре, още подробности:

Имам IWorker-интерфейс, който всичките ми процеси изпълняват. Единичен WorkerFactory ми позволява да създавам нови екземпляри на всеки работник, имплементиран във всеки сбор в рамките на същия път като текущия. За да направя това, добавих CreateWorker-метод към моя интерфейс, който приема масив от низове (които са предадени като varargs на конзолното приложение).

Преди да мога да създам какъвто и да е екземпляр на всеки работник, трябва да регистрирам който и да е клас във фабриката. За съжаление на C# не е възможно да се вмъкне такова поведение статично във всеки статичен контекст на работните класове, тъй като кодът в този контекст се изпълнява само когато класът е достъпен по какъвто и да е начин (било то чрез инстанциране или чрез достъп до някой от неговите статични членове, вижте статични конструктори). Така че постигам това, като отразявам всички типове, открити във всички сглобки, с пътя на текущо изпълняващия се сглобка. Сега мога да проверя дали текущият тип прилага интерфейса и ако е така, типът може да бъде добавен към регистрационната карта на фабриката, съдържаща името на операцията (ключ) и работника (стойност) като System.Type (вижте Фабричен модел, използващ отражение). Проблемът е: как да получа действителното име на операцията, към която се отнася този тип.

Мисля, че има два варианта за това:

  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