views:

71

answers:

4

Currently in code i have used an object factory to return me a processor based of a string tag, which has severed its purpose up until now.

using Core;
using Data;

public static class TagProcessorFactory
{
 public static ITagProcessor GetProcessor(string tag)
 {
    switch (tag)
    {
       case "gps0":
         return new GpsTagProcessor();
       case "analog_manager":
         return new AnalogManagerTagProcessor();
       case "input_manager":
         return new InputManagerTagProcessor();
       case "j1939":
         return new J1939TagProcessor(new MemcachedProvider(new[] {  "localhost" }, "DigiGateway"), new PgnRepository());
       default:
         return new UnknownTagProcessor();
    }
 }
}

Calling Code

var processor = TagProcessorFactory.GetProcessor(tag.Name);

if (!(processor is UnknownTagProcessor))
{
    var data = processor.Process(unitId, tag.Values);

    Trace.WriteLine("Tag <{0}> processed. # of IO Items => {1}".FormatWith(tag.Name, data.Count()));
}

as you can see one of my items has dependencies and im trying to execute testing code and i want to pass in mock repositories and cache providers but i can seem to think of a way to do this.

Is this a bad design or anyone have any ideas to fix it to make my factory testable?

Thanks

A: 

Using something like StructureMap you could use the ObjectFactory which, when configured would return you a named concrete instance.

http://structuremap.net/structuremap/index.html

brumScouse
A: 

One possibility is to define the dependencies as optional arguments:

public static ITagProcessor GetProcessor(string tag, MemcachedProvider mcp = null, PgnRepository pr = null) {

// ... other code

case "j1939": 
    return new J1939TagProcessor(mcp, pr); 

// ... other code

}

You can take things even a step further if you want: by creating a separate context class that the factory uses to inject dependencies into the required object.

public static ITagProcessor GetProcessor(ITagProcessorCtx ctx) { 

string tag = ctx->getTag();

// ... other code 

case "j1939":  
    return new J1939TagProcessor(ctx->getMcp(), ctx->getPr());  

// ... other code 

}
Saul
A: 

I suggest you look through another SO post. It solves several problems at once, including how to replace contructor values - without a mess. Specifically, the parameters to the constructor simply become static fields of a "Context" class, which are read by the constructor of the interior class.

Brent Arias
+1  A: 

Since you are using Autofac, you can take advantage of the lookup relationship type:

public class Foo
{
    private readonly IIndex<string, ITagProcessor> _tagProcessorIndex;

    public Foo(IIndex<string, ITagProvider> tagProcessorIndex)
    {
        _tagProcessorIndex = tagProcessorIndex;
    }

    public void Process(int unitId, Tag tag)
    {
        ITagProcessor processor;

        if(_tagProcessorIndex.TryGetValue(tag.Name, out processor))
        {
            var data = processor.Process(unitId, tag.Values);

            Trace.WriteLine("Tag <{0}> processed. # of IO Items => {1}".FormatWith(tag.Name, data.Count()));
        }
    }
}

See the TypedNamedAndKeysServices wiki article for more information. To register the various processors, you would associate each with its key:

builder.RegisterType<GpsTagProcessor>().Keyed<ITagProcessor>("gps0");
builder.RegisterType<AnalogManagerTagProcessor>().Keyed<ITagProcessor>("analog_manager");
builder.RegisterType<InputManagerTagProcessor>().Keyed<ITagProcessor>("input_manager");

builder
    .Register(c => new J1939TagProcessor(new MemcachedProvider(new[] {  "localhost" }, new PgnRepository()))
    .Keyed<ITagProcessor>("j1939");

Notice we don't register UnknownTagProcessor. That was a signal to the caller of the factory that no processor was found for the tag, which we express using TryGetValue instead.

Bryan Watts
i like this approach. Only think i cant figure out is how to resolve the IIndex<string, ITagProvider> from the builder to pass into the constructor of Foo. Help?
Waterboy4800
Autofac's relationship types are automatic. If you just call `c.Resolve<IIndex<string, ITagProcessor>>()`, the container will interpret that request and create the appropriate implementation for you. This is true even when you use `builder.RegisterType<Foo>()`. As another example, if you were to accept `Func<SomeType>`, the container will recognize the relationship type and generate a function which resolves `SomeType`. It is quite nifty.
Bryan Watts
pretty cool. thanks alot!
Waterboy4800
@Waterboy4800: Be sure to mark the answer as accepted if it solves your problem.
Bryan Watts