4

I have a system that performs operations on lots of Things, these can be considered to be hardware devices accessible over a communication channel.

I use a manager construct that accepts tasks for single Things. Now, there are at least three types of Thing and they have slightly different properties associated with them. The manager must know about these extra properties as they are needed to perform any operation correctly (some Things must have their X foo'd instead of their Y etc...).

At the moment I have separate manager class for each type of thing. This causes a good deal of duplication as the Things are mostly similar.

It would be good if I could have an abstract manager that implements a good deal of the functionality and then each concrete implementation can supply the little extra bits.

Here is a greatly simplified example:

public abstract class ThingManager
{
    private ConcurrentDictionary<Guid, ??ThingTask??> _ThingTaskQueue;

    public virtual AddNewThingTask(<params>)
    {
        ??ThingTask?? NewTask = new ??ThingTask??(<params>);
        _ThingTaskQueue.Add(NewTask);
        Monitor.Pulse(_NewDataToProcess);
    }

    /* Implemented by the concrete, will depend on the type of ??ThingTask?? */
    public abstract GetSomeTaskParameterForAThing(Guid thingID)   
}

public class ThingTask
{
    public enum ThingOperation
    {
        Foo,
        Bar 
    };

    public String Name { get; set; };
    public ThingType Type { get; set; };
    public ThingOperation Operation { get; set; } 
}

public class AdvancedThingTask
{
    public enum ThingOperation
    {
        Foo,
        Bar,
        Baz 
    };

    public String Name { get; set; };
    public ThingType Type { get; set; };
    public ThingOperation Operation { get; set; }
    public Boolean EnableFrobber { get; set; } 
}

As you can see I need some way, when defining the concrete ThingManager to have ??ThingTask?? be either a ThingTask or an AdvancedThingTask. It would then be up to the concrete to make use of the extra properties when implementing the abstract methods.

Using an interface for ??ThingTask?? wouldn't work because the properties would have to be declared in the interface and each one has different properties available.

I get the feeling I'm missing something very obvious as to how to do this cleanly, hopefully someone can help :)

marc_s
  • 732,580
  • 175
  • 1,330
  • 1,459
Cursorkeys
  • 43
  • 5

2 Answers2

3

use generics rather than a pure abstract class, someting along the lines of:

public abstract class ThingManager<T> where T : ThingTask

dependant on your full implementation I doubt if this will need to remain abstract

dice
  • 2,820
  • 1
  • 23
  • 34
  • Thank you very much for your comment. I've just Googled Generic Classes and this looks like powerful stuff. I'll read up on this immediately. – Cursorkeys Feb 27 '12 at 17:28
  • They are called generics in C# and are different from C++ templates – Rune FS Feb 27 '12 at 17:48
  • Oh wow, Generics are great. I have the whole thing implemented and am using the Reflection method shown in [link](http://stackoverflow.com/a/3054835/1228201) to pull suitable constructors out of my generic type parameters. It's a shame it's working now, this is great fun :) – Cursorkeys Feb 28 '12 at 09:30
  • glad it worked out - one thing to note, reflection will have a performance impact (maybe not large in this case) - you can achieve the same thing using "public abstract class ThingManager where T : ThingTask, new()" and then "T newTask = new T();" – dice Feb 28 '12 at 09:40
  • Thanks for the tip, I did consider the performance impact but it seems to be very small (luckily) for my situation. I tried your suggestion but it seems to only let me use the parameterless constructor. Although Googling your suggestion did show me about the "Activator.CreateInstance" method which seems to do the same as the Reflection in a single line. – Cursorkeys Feb 28 '12 at 10:10
0

Is there any reason you don't make AdvancedThingTask a subclass of ThingTask?

public class ThingTask
{
  public virtual string Name { get; set; }
  public virtual ThingType Type { get; set; }
  public virtual ThingOperation Operation { get; set; }

  public virtual void DoThing() { /*Do something associated with ThingTask*/ }
}

public class AdvancedThingTask : ThingTask
{
  public bool EnableFrobber { get; set; }

  public override void DoThing() { /*Do something associated with AdvancedThingTask*/ }
}

The only problem I see with this is that ThingOperation will need to be declared outside of the classes so that it can have all the values, or some other solution that will enable classes to have values that aren't in the base declaration. That problem may be solved by putting what you want to do as virtual methods in the classes.

P.S. Why do your properties start with underscores? Usually that's reserved for private variables.

Kyle W
  • 3,702
  • 20
  • 32
  • Yeah, the ThingOperation enum will be different between the Task classes so I didn't think subclassing was the best way. The underscores are left over from anonymizing, they were originaly seperate private variables with separate public setters and getters. – Cursorkeys Feb 27 '12 at 17:36
  • @Cursorkeys You can make 'ThingOperation Operation' not virtual and remove it from the base class, or even make it private so that it can only be used from DoThing() or similar – Kyle W Feb 27 '12 at 17:43