我的应用程序使用连接到PC的测量仪器。我希望能够使用来自不同供应商的类似仪器。
所以我定义了一个界面:
interface IMeasurementInterface
{
void Initialize();
void Close();
}
到目前为止一切顺利。在测量之前,我需要设置仪器,这意味着不同仪器的参数非常不同。所以我想定义一个方法,它接受可以有不同结构的参数:
interface IMeasurementInterface
{
void Initialize();
void Close();
void Setup(object Parameters);
}
然后我会将对象转换为我需要的任何东西。这是要走的路吗?
答案 0 :(得分:13)
你可能最好想出一个抽象的“参数”类,它由每个不同的仪器参数扩展...例如然后使用Generics确保将正确的参数传递给正确的类......
public interface IMeasurement<PARAMTYPE> where PARAMTYPE : Parameters
{
void Init();
void Close();
void Setup(PARAMTYPE p);
}
public abstract class Parameters
{
}
然后针对每个特定设备,
public class DeviceOne : IMeasurement<ParametersForDeviceOne>
{
public void Init() { }
public void Close() { }
public void Setup(ParametersForDeviceOne p) { }
}
public class ParametersForDeviceOne : Parameters
{
}
答案 1 :(得分:2)
对我而言,Factory模式听起来可能很有用,特别是如果您要对您的应用进行单元测试。
答案 2 :(得分:1)
如果您打算处理多种设备类型,那么使用Name vlaue对进行通信的控制器+设备接口分离将是一个很好的解决方案
<强>解耦强>
使用名称值对可以将代码分成设备+控制器+应用程序代码结构
示例代码
class DeviceInterface
{
void Initialize(IController & Controller);
void Close();
bool ChangeParameter(const string & Name, const string & Value);
bool GetParam(string & Name, string &Value );
}
每个设备实现在创建时都应该使用可以接受其命令并将其转换为实际设备命令的控制器标识来创建
interface IController
{
Initialize(DeviceSpecific & Params);
Close();
bool ChangeParameter(string & Name, string & Value);
bool ChangeParams(string & Name[], string &Value []);
}
您的用户代码看起来像这样
IController objController = new MeasurementDevice(MeasureParram);
DeviceInterface MeasureDevice = new DeviceInterface(objController);
string Value;
MeasureDevice.GetParam("Temperature", Value);
if (ConvertStringToInt(Value) > 80)
{
MeasureDevice.ChangeParameter("Shutdown", "True");
RaiseAlert();
}
DeviceInterface类应该做的就是将命令传递给控制器。控制器应该负责设备通信。
界面分离的优点
保护更改
这种解耦将允许您将应用程序代码与控制器隔离。设备更改不会影响您的用户代码
申请代码的可维护性
此外,用户代码始终是干净的,您只需要使用应用程序逻辑。但是,如果您使用特定于控制器的多种类型的参数结构定义了多个接口/创建的模板或泛型,那么您的代码中将包含许多依赖于设备的垃圾,这可能会损害可读性并在您的设备/其参数发生变化时产生维护问题。
实施轻松
您还可以将不同的控制器实现分配到自己的项目中。此外,您的应用程序还可以使用XML文件等以更动态的方式配置命令和响应,这些文件可以与控制器类一起发布,从而使您的整个应用程序本质上变得更加动态。
现实生活
该领域领导者的最新生产控制器项目之一以相同的方式运作。但他们使用LON进行设备通信。
LON?
控制器中使用的LON协议(想想空调/锅炉/风扇等)网络使用这个概念与各种设备通信
所以您需要拥有的是一个可以与您的设备通信的接口,然后使用LON将名称值对发送给它。他使用标准协议还允许您与测量仪器以外的其他设备通信。如果您的设备使用LON,则可以使用LON的开源实现。
如果您的设备不支持LON,那么您可能必须设计一些用户代码仍在名称值对上工作的东西,而另一个接口将您的名称值对转换为等效的对应cotroller struct +并与之相关的个人设备进行通信。设备理解的方式。
希望这很有用。
答案 3 :(得分:1)
这取决于你如何获得参数。如果它们存储在某个数据库表或配置文件中,并且它只是需要设置的值,那么传入字典可能会这样做(尽管你确实失去了类型安全性)。如果您的设置过程稍微复杂一点,那么我会考虑进一步抽象设置过程并执行双重调度(将强制转换操作推送到新的安装类)。喜欢这个
public interface IMeasurementInterface
{
void Initialize();
void Close();
void Setup( IConfigurer config );
}
public interface IConfigurer
{
void ApplyTo( object obj );
}
public abstract ConfigurerBase<T> : IConfigurer where T : IMeasurementInterface
{
protected abstract void ApplyTo( T item );
void IConfigurator.ApplyTo(object obj )
{
var item = obj as T;
if( item == null )
throw new InvalidOperationException("Configurer can't be applied to this type");
ApplyTo(item);
}
}
通过这种方式,您不会弄乱您的Measurement类层次结构(或者不提供任何实现,并假设所有实现都可以执行您想要的操作)。这也意味着您可以通过传入假(或模拟)测量设备来测试您的安装代码。
如果设置过程需要操作私有或受保护的数据,那么您可以使IConfigurer的具体实现驻留在其相应的Measurement类中。
答案 4 :(得分:1)
我需要为我的软件使用这个,因为我需要为金属切割机支持许多不同类型的运动控制器。
您的界面具有您需要的基础知识。您需要记住的是,您不需要传入参数列表。您指出每种类型的设备可能具有非常不同类型的设置。
我这样做的方式如下
interface IMeasurementInterface
{
void Initialize();
void Close();
void Setup();
void Read (FileReader as <whatever read file object you are using>)
void Store (FileReader as <whatever read file object you are using>)
string Name();
}
安装程序调用在IMeasurementDevice程序集中创建的对话框。该对话框在程序集外部不可见。
现在我知道一些面向对象或MVC纯粹主义者可能会反对这一点。但是我觉得隐藏特定Measurement类的内部结构的概念超过了对MVC架构的严格遵守。
我的一般理念是trival对话框在同一个程序集中实现,前提是它对程序集是私有的,并由在我设置的标准接口上实现的对象调用。再次出现这种情况的原因是我发现隐藏内部结构比尝试在顶级程序集中实现所有对话框更有价值。
通过指定Read Method和Store方法,您无需公开内部设置参数进行保存。您只需传递用于保存设置参数的任何类型的文件存储对象。
最后,如同另一张海报所述,您需要在装配中设置包含所有测量设备的工厂类。在设置过程中,您需要实例化此类并检索支持的测量设备列表。
我这样做的方法是我的工厂类检索一个运动控制器列表。此列表是存储所有安装类的主类的一部分。当我读取我的安装文件时,我得到了实际使用的控制器。我从列表中检索这些类,并将它们放在切割过程中实际使用的另一个列表中。
我这样做的原因是,当用户设置运动控制器时,他需要能够从所有可用控件的列表中选择,以告诉软件他有哪一个。我发现保持可用控制器列表的响应速度更快。
答案 5 :(得分:0)
那可能会奏效。另一种选择是在字典中传递参数。