这个方法是否返回一个System.对象类是一个反模式
本文关键字:一个 模式 对象 System 是否 方法 返回 | 更新日期: 2023-09-27 18:03:13
我在一个设计电子元件测试的自动化团队工作。我们的框架非常需要的一件事是为工作台中各种测试设备的驱动对象提供一个单一的源点(现在,驱动对象的创建非常狂野)。
基本上,这个想法将会有一个对象,基于一个配置文件构造,这是所有其他测试代码基于名称字符串查找以获得驱动程序对象的单一位置。我把它命名为"DriverSource"。
问题是,这些驱动程序根本不提供类似的接口。一个可能是电源(使用"SetVoltage"answers"SetCurrentLimit"等方法),而另一个可能是数字万用表(使用"ReadVoltage"或"ReadCurrent"等方法)。
我想到的最好的解决方案是用如下声明的方法:
public object GetDriver(string name);
然后,使用我的"DriverSource"对象的测试代码将调用该方法,然后强制转换System。对象到正确的驱动程序类型(或者更准确地说,是正确的驱动程序接口,如IPowerSupply)。我认为这样的类型转换是可以接受的,因为无论测试代码要使用这个驱动程序,最好都知道接口是什么。但我希望能得到一些反馈,看看这是不是一个等着咬我的反模式。如果有更好的模式来解决这个问题,我将不胜感激。
最后的提示:我认为这是显而易见的,但性能基本上不是这个问题的问题。在持续数小时的测试运行中,获取驱动程序的次数少于100次。
如果您已经知道类型,并且无论如何都要强制转换为接口或类,那么更好的方法是为方法调用提供类型参数。
public T GetDriver<T>(string name);
然后,您可以使用工厂模式从该方法返回适当类型的对象。
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);
}
}
用法:
var driver = GetDriver<Foo>(someString); // Returns a Foo object
如果你真的想让它通用,我会使用工厂模式。
让我们从识别类型结构开始:
public interface IDriver
{
}
public interface IPowerSupply : IDriver
{
void SetVoltage();
void SetCurrent();
}
public interface IMultimeter : IDriver
{
double MeasureVoltage();
}
可以根据需要添加或删除。现在我们需要一种方法让工厂自动发现正确的类型并向其提供配置信息。让我们创建一个自定义属性:
public class DriverHandlerAttribute : Attribute
{
public Type DriverType { get; set; }
public string ConfigurationName { get; set; }
}
然后我们需要一个地方来存储配置数据。该类型可以包含任何您想要的内容,例如从配置文件中加载的键/值字典:
public class Configuration
{
public string DriverName { get; set; }
public string OtherSetting { get; set; }
}
最后我们可以创建一个驱动程序。让我们创建一个IPowerSupply
:
[DriverHandler(DriverType = typeof(IPowerSupply), ConfigurationName="BaseSupply")]
public class BasePowerSupply : IPowerSupply
{
public BasePowerSupply(Configuration config) { /* ... */ }
public void SetVoltage() { /* ... */ }
public void SetCurrent() { /* ... */ }
}
重要的部分是它被属性装饰,并且它有一个构造函数(尽管我创建了工厂,以便它也可以使用默认构造函数):
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;
}
}
因此,要使用它,您将读入配置数据(从XML加载,从服务读取,文件等)。然后您可以像这样创建驱动程序:
var driver = DriverFactory.Create(configuration);
或者,如果您正在使用通用方法,并且您知道配置是用于电源的,则可以调用:
var driver = DriverFactory.Create<IPowerSupply>(configuration);
当您运行测试时,您可以验证是否返回了正确的数据,例如,在您的测试方法中:
Assert.IsTrue(driver is IPowerSupply);
Assert.IsTrue(driver is BaseSupply);
Assert.DoesWhatever(((IPowerSupply)driver).SetVoltage());
等等
我将使用以下代码:
public T GetDriver<T>(string name)
{
return ((Func<string, T>)_factories[typeof(T)])(name);
}
_factories
对象如下所示:
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()) },
};
基本上_factories
字典包含了基于传入的字符串参数创建每个对象类型的所有代码。注意,在上面的例子中,Foo
类接受s
作为构造函数参数。
字典还可以在运行时修改,以满足您的需要,而无需重新编译代码。
我甚至会更进一步。如果你定义了这个工厂类:
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);
}
}
你可以这样写:
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");
我更喜欢那个。它是强类型的,可以在运行时轻松定制。