Get applicable interface implementation

111 views Asked by At

I'm trying to find a good design to a thing I'm working on. As an example, consider a TextProcessor class that accepts a string parameter and returns back a processed string.

Now in my implementation there are a number of different TextProcessors, each capable handling their own defined set of strings. There are two notable methods in each processor:

bool CanProcess(string text);

and

string Process(string text);

In reality these can be marked as static but for the sake of implementing a common interface I'm not setting them as static.

In addition to these TextProcessors there is a static TextProcessorFinder class. As the name implies, it finds the best TextProcessor and processes the input.

public static class TextProcessorFinder
{
    private static List<ITextProcessor> _processors;

    static TextProcessorFinder()
    {
        _processors = Assembly.GetExecutingAssembly().GetTypes()
            .Where(t => t.GetInterfaces().Contains(typeof(ITextProcessor))
            && t.IsClass && !t.IsAbstract)
            .Select(t => (ITextProcessor)Activator.CreateInstance(t))
            .Where(t => t.IsActive)
            .ToList();
    }

    public static ITextProcessor GetTextProcessor(string text)
    {
        return _processors.Where(p => p.CanProcess(text))
            .OrderByDescending(p => p.Priority)
            .FirstOrDefault();
    }
}

What I hate about this approach is that I have to create an instance of every known TextProcessor just to call their CanProcess function.

I have tried creating an attribute with a simple Func to emulate the CanProcess function:

[AttributeUsage(AttributeTargets.Class)]
public class TextProcessorAttribute : Attribute
{
    private Func<string, bool> func;

    public TextProcessorAttribute(Func<string, bool> func)
    {
        this.func = func;
    }
}

public interface ITextProcessor
{
    bool IsActive { get; }

    int Priority { get; }

    bool CanProcess(string text);

    string Process(string text);
}

// Hard-coded to true
[TextProcessor((s) => { return true; })]
public class SampleTextProcessor : ITextProcessor
{
    // Implement ITextProcessor
}

Sadly however, Func is not a valid attribute parameter type.

What is the best way of doing this?

2

There are 2 answers

2
Daniel Hilgarth On BEST ANSWER

In my opinion, creating those instances is the best way to go. There really is no reason to not do it. Instances are cheap and creating them is cheap as well if you are not doing any work in the constructors - which you shouldn't do anyway.

1
Pedro Perez On

With the CanProcess method you ask to each derived ITextProcessor if he can process the text. This is OK in my opinion. Maybe there are some sort of specialization in the implementations of ITextProcessor, in this case you can create more specific interfaces, ITextTypeAProcessor, ITextTypeBProcessor, etc. Then you can filter by interface in your GetTextProcessor method.