HimBromBeere HimBromBeere - 11 days ago 5
C# Question

Factory pattern using reflection - class registration using static key associated with each class

While studying some patterns I came across the factory pattern to create instances of (unknown) classes. That made me interested hence I want to build a program that can be dynamically enhanced. So I have some basic functionality within one assembly and the actual workers within different assemblies. The only hint that I get from my application is an operation-name. Now I want to create a new worker that relies to the operation. Because I do not know the actual implementations of the workers I've chosen this kind of pattern.

OK, some more details:

I have an

IWorker
-interface that all my processes implement. A Singleton
WorkerFactory
lets me create new instances of any worker implemented in any assembly within the same path as the current one. To do so I added a
CreateWorker
-method to my interface that accept an array of strings (which have been passed as
varargs
to the console-application).

Before being able to create any instance of any worker I have to register any class to the factory. Unfortunately on C# it is not possible to insert such a behaviour statically into any static context of the worker-classes because the code within this context is only executed when the class is accessed in any way (be it by instantiating it or by accessing any of its static members, see static constructors). So I achieve this by reflecting all the types found within all assemblies with the path of the currently executing assembly.
Now I may check if the current type is implementing the interface and if so the type can be added to the registration-map of the factory containing the operations name (key) and the worker (value) as
System.Type
(see Factory Pattern using reflection). The problem is: how do I get the actual name of the operation this type refers to.

I think there are two options for this:


  1. I add a static property to every worker-class that stores the operations name. Thus I can refer the operation by reflecting this property on the obtained type.

  2. Adding a non-static property for the name and create a dummy-instance of the actual worker-class while registering.



While the former option has the disadvantage that I cannot ensure that all workers actually have such a static property implemented (hence it is not possible to add a static member to the IWorker-interface) the latter has the disadvantage that every class must have an empty constructor in order to build the dummy-instance from which to get the operations name. Nevertheless this constructor would be publicly accessable whilst not containing any initialization-code.

Having said that all this question is about best or at least better practices on factory pattern using reflection. I think I´d prefer the first option, but maybe I also missed any better solution for this approach.

Hence this thread is already quite long I´d like not to post any code here, but let me know if you need anyway.

Answer

You can use custom attributes on your worker classes:

[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
{
}

They are accessed like it is described here. It will be something like:

       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"]);

It is not the best and does not cover multiple WorkerAttributes on the class, but can be used as the basis for your solution.

Comments