Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is this method returning a System.Object class an anti-pattern?

Tags:

c#

I work an an automation team designing tests for electronic components. One thing our framework sorely needs is a single source point for our driver objects for the various pieces of test equipment at a workbench (right now, driver object creation is very wild-west).

Basically, the idea would be there would be one object, constructed based on a configuration file(s), which is the single place all other test code looks to to get the driver objects, based on a name string. I'll call it a "DriverSource" here.

The problem is, these drivers do not present similar interfaces at all. One might be a power supply (with methods like "SetVoltage" and "SetCurrentLimit"), while another might be a digital multimeter (with methods like "ReadVoltage" or "ReadCurrent").

The best solution I've come up with is to have a method with the following declaration:

public object GetDriver(string name);

Then, the test code using my "DriverSource" object would call that method, and then cast the System.Object to the correct driver type (or more accurately, the correct driver interface, like IPowerSupply).

I think casting like that is acceptable because whatever test code is about to use this driver had better know what the interface is. But I was hoping to get some input on whether or not this is an anti-pattern waiting to bite me. Any better pattern for solving this issue would also be greatly appreciated.

A final note: I think this is obvious, but performance is essentially a non-issue for this problem. Fetching the drivers is something will happen less than 100 times in a test run that can last hours.

like image 879
AHall1111 Avatar asked Jul 17 '15 02:07

AHall1111


3 Answers

If you already know the type and you're going to cast to an interface or class anyway, a better approach would be to hand the method call a type parameter.

public T GetDriver<T>(string name);

You can then use a Factory pattern to return you an object of the appropriate type from the method.

public T GetDriver<T>(string name)
{
    switch(typeof(T).Name)
    {
        case "Foo":
            // Construct and return a Foo object
        case "Bar":
            // Construct and return a Bar object
        case "Baz":
            // Construct and return a Baz object
        default:
            return default(T);
    }
}

Usage:

var driver = GetDriver<Foo>(someString); // Returns a Foo object
like image 56
Robert Harvey Avatar answered Nov 12 '22 08:11

Robert Harvey


If you really want to make this generic, I would use a factory pattern.

Lets start off by identifying the type structure:

public interface IDriver
{
}

public interface IPowerSupply : IDriver
{
    void SetVoltage();
    void SetCurrent();
}

public interface IMultimeter : IDriver
{
    double MeasureVoltage();
}

Which you can add to or remove from as needed. Now we need a way for the factory to auto-discover the correct types and provide the configuration information to it. So lets create a custom attribute:

public class DriverHandlerAttribute : Attribute
{
    public Type DriverType { get; set; }
    public string ConfigurationName { get; set; }
}

And then we need a place to store configuration data. This type can contain whatever you want, like a dictionary of keys/values that are loaded from configuration files:

public class Configuration
{
    public string DriverName { get; set; }
    public string OtherSetting { get; set; }
}

Finally we can create a driver. Lets create an IPowerSupply:

[DriverHandler(DriverType = typeof(IPowerSupply), ConfigurationName="BaseSupply")]
public class BasePowerSupply : IPowerSupply
{
    public BasePowerSupply(Configuration config) { /* ... */ }

    public void SetVoltage() { /* ... */ }

    public void SetCurrent() { /* ... */ }
}

The important part is that it is decorated with the attribute and that it has a constructor (although I created the factory so that it can use default constructors too):

public static class DriverFactory
{
    public static IDriver Create(Configuration config)
    {
        Type driverType = GetTypeForDriver(config.DriverName);
        if (driverType == null) return null;
        if (driverType.GetConstructor(new[] { typeof(Configuration) }) != null)
            return Activator.CreateInstance(driverType, config) as IDriver;
        else
            return Activator.CreateInstance(driverType) as IDriver;
    }

    public static T Create<T>(Configuration config) where T : IDriver
    {
        return (T)Create(config);
    }

    private static Type GetTypeForDriver(string driverName)
    {
        var type = (from t in Assembly.GetExecutingAssembly().GetTypes()
                    let attrib = t.GetCustomAttribute<DriverHandlerAttribute>()
                    where attrib != null && attrib.ConfigurationName == driverName
                    select t).FirstOrDefault();
        return type;
    }
}

So to use this, you would read in the configuration data (loaded from XML, read from a service, files, etc). You can then create the driver like:

var driver = DriverFactory.Create(configuration);

Or if you are using the generic method and you know the configuration is for a power supply, you can call:

var driver = DriverFactory.Create<IPowerSupply>(configuration);

And when you run your tests, you can verify that you get the right data back, for example, in your test method:

Assert.IsTrue(driver is IPowerSupply);
Assert.IsTrue(driver is BaseSupply);
Assert.DoesWhatever(((IPowerSupply)driver).SetVoltage());

And so-on and so-forth.

like image 2
Ron Beyer Avatar answered Nov 12 '22 08:11

Ron Beyer


I would go with this code:

public T GetDriver<T>(string name)
{
    return ((Func<string, T>)_factories[typeof(T)])(name);
}

The _factories object looks like this:

private Dictionary<Type, Delegate> _factories = new Dictionary<Type, Delegate>()
{
    { typeof(Foo), (Delegate)(Func<string, Foo>)(s => new Foo(s)) },
    { typeof(Bar), (Delegate)(Func<string, Bar>)(s => new Bar()) },
    { typeof(Baz), (Delegate)(Func<string, Baz>)(s => new Baz()) },
};

Basically the _factories dictionary contains all of the code to create each object type based on string parameter passed in. Note that in my example above the Foo class takes s as a constructor parameter.

The dictionary can also then be modified at run-time to suite your needs without needing to recompile code.


I would even go one step further. If you define this factory class:

public class Factory
{
    private Dictionary<Type, Delegate> _factories = new Dictionary<Type, Delegate>();

    public T Build<T>(string name)
    {
        return ((Func<string, T>)_factories[typeof(T)])(name);
    }

    public void Define<T>(Func<string, T> create)
    {
        _factories.Add(typeof(T), create);
    }
}

You can then write this code:

var drivers = new Factory();
drivers.Define(s => new Foo(s));
drivers.Define(s => new Bar());
drivers.Define(s => new Baz());

var driver = drivers.Build<Foo>("foo");

I like that even better. It's strongly-typed and easily customized at run-time.

like image 2
Enigmativity Avatar answered Nov 12 '22 08:11

Enigmativity