0

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

Adam Crossland
  • 14,198
  • 3
  • 44
  • 54
Waterboy4800
  • 232
  • 2
  • 10
  • 1
    Is there some compelling reason that you decided not to use one of the many, many Free and excellent dependency injection containers available for C#? Any of them could handle this injection pattern gracefully. – anthony Oct 28 '10 at 19:54
  • i am. i'm using autofac, my problem is i did not want to pass in the builder/container to resolve the dependency, iv read not to pass the container around. – Waterboy4800 Oct 28 '10 at 19:57
  • I would say passing a factory around is acceptable as the class is requesting a deferred creation of an object. – aqwert Oct 28 '10 at 23:51
  • A bit unrelated, but why are you testing for the `UnknownTagProcessor`? Why not either: 1) thrown an exception instead of returning a value, or 2) return a Null Object that you can simply call `Process` on? This seems like a much cleaner design. – Steven Oct 29 '10 at 09:20
  • it was at first throwing an exception, but there was a lot of unknown tags i did not want to bother with which was throwing a lot of exceptions. So I threw the unknown tag processor in there in which i was just calling process on which was meant to log that there was a tag without a processor, but then i lost the context of which tag unless i pass the tag name into the process method. Your right its not clean right now and I'm still thinking of the best way to wrap it up. – Waterboy4800 Oct 29 '10 at 13:44
  • Pass the tag name into the constructor of the UnknownTagProcessor. – WW. Nov 02 '10 at 07:00

3 Answers3

3

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
  • 44,911
  • 16
  • 83
  • 88
  • i like this approach. Only think i cant figure out is how to resolve the IIndex from the builder to pass into the constructor of Foo. Help? – Waterboy4800 Oct 29 '10 at 19:36
  • Autofac's relationship types are automatic. If you just call `c.Resolve>()`, the container will interpret that request and create the appropriate implementation for you. This is true even when you use `builder.RegisterType()`. As another example, if you were to accept `Func`, the container will recognize the relationship type and generate a function which resolves `SomeType`. It is quite nifty. – Bryan Watts Oct 29 '10 at 20:25
0

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
  • 3,166
  • 1
  • 24
  • 38
0

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.

Community
  • 1
  • 1
Brent Arias
  • 29,277
  • 40
  • 133
  • 234