【问题标题】:Define an interface method that takes different parameters定义一个接受不同参数的接口方法
【发布时间】:2010-09-19 06:52:26
【问题描述】:

我的应用程序使用连接到 PC 的测量仪器。我想让使用来自不同供应商的类似仪器成为可能。

所以我定义了一个接口:

interface IMeasurementInterface
    {
        void Initialize();
        void Close();
    }

到目前为止一切顺利。在测量之前,我需要设置仪器,这对于不同的仪器来说意味着非常不同的参数。所以我想定义一个方法,它接受可以有不同结构的参数:

interface IMeasurementInterface
{
    void Initialize();
    void Close();
    void Setup(object Parameters);
}

然后我会将对象转换为我需要的任何东西。这是要走的路吗?

【问题讨论】:

  • 您打算对各种具体的仪器类进行单元测试,还是使用模拟器和硬件验证?

标签: c# .net oop


【解决方案1】:

您最好提出一个抽象的“参数”类,该类由每个不同的仪器参数扩展......例如然后使用泛型确保将正确的参数传递给正确的类...

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
{

}

【讨论】:

  • 参数应该是接口吧?我更喜欢描述“配置”而不是设置。
  • 感谢您的回答,但这又引出了另一个问题。在我的模型中,我有一个名为的私有字段:MeasurementDevice,它的类型应该是 IMeasurement 如果我想启动 DeviceOne,我会这样做:MeasurementDevice = new DeviceOne() 这不起作用(无法转换 DeviceOne。
  • 当你创建一个设备时,你需要知道通过接口持有对它的引用的设备类型,这样你就失去了抽象的力量
  • 非常好的方法。我曾经将这些参数移动到构造函数,每次您需要调用Setup() 时,您都需要创建新对象。这样您就不需要另一组 Parameter 类。
【解决方案2】:

在我看来,Factory 模式可能很有用,尤其是当您要对应用进行单元测试时。

【讨论】:

    【解决方案3】:

    如果您要处理不止一种设备类型,那么控制器 + 设备接口分离(使用名称值对进行通信)将是一个很好的解决方案

    解耦

    使用名称值对允许您将代码分隔为设备 + 控制器 + 应用程序代码结构

    示例代码

    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,那么您可能必须设计一些东西,其中用户代码仍然适用于名称值对,并且相反的接口将您的名称值对转换为等效的对应控制器 struct+ 并与设备理解的方式。

    希望这有用。

    【讨论】:

      【解决方案4】:

      这首先取决于您将如何获取参数。如果它们存储在某个数据库表或配置文件中,并且只是需要设置的值,那么传入字典可能会这样做(尽管您确实会失去类型安全性)。如果您的设置过程会稍微复杂一点,那么我会考虑进一步抽象设置过程并执行双重调度(将转换操作推入新的设置类)。像这样

      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 类中。

      【讨论】:

        【解决方案5】:

        我的软件必须这样做,因为我需要支持许多不同类型的金属切割机运动控制器。

        您的界面具有您需要的基础知识。您需要记住的是,您不需要传入参数列表。您指出每种类型的设备可能具有非常不同的设置类型。

        我的做法如下

        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 纯粹主义者可能会反对这一点。然而,我觉得隐藏特定测量类内部的概念胜过严格遵守 MVC 架构。

        我的一般理念是,简单对话框在同一个程序集中实现,前提是它对程序集是私有的,并且由在我设置的标准接口上实现的对象调用。再次这样做的原因是,我发现隐藏内部结构比尝试在顶层程序集中实现所有对话框更有价值。

        通过指定读取方法和存储方法,您无需公开内部设置参数以进行保存。您只需传递您用来保存设置参数的任何类型的文件存储对象。

        最后就像另一张海报所说,您需要在包含所有测量设备的程序集中设置一个工厂类。在设置期间,您需要实例化此类并检索支持的测量设备列表。

        我这样做的方式是我的工厂类检索运动控制器列表。此列表是存储所有设置类的主类的一部分。当我阅读我的设置文件时,我得到了实际使用的控制器。我从列表中检索出这些类,并将它们放在另一个在切割过程中实际使用的列表中。

        我这样做的原因是,当用户设置运动控制器时,他需要能够从所有可用控件的列表中进行选择,以便告诉软件他拥有哪一个。我发现保留可用控制器列表的响应速度更快。

        【讨论】:

        • 这可以工作,但是我需要从 UI 传递一些配置参数,而不打开对话框。如果我只想更改要测量的点数,我想从主 UI 而不是从设置对话框中执行此操作。因此我希望安装程序接受参数
        • 在我们的软件中,我们有商店标准,可从软件中的任何位置访问。我建议将该参数(以及其他类似参数)放入您的商店标准版本中。
        • 哪个参数在我使用规则的地方 1) 如果它属于同一类的所有设备。加入全球商店标准。 2)如果它特定于设备,请将其隐藏在设备界面后面。在其他情况下,您不需要通过设置传递额外的参数。
        • 但是您确实需要传递特定的参数,那么我建议在界面中添加一个 CustomSetup 过程。它将被定义为 Sub CustomSetup(Parameter_Name as String, Value as String)。
        【解决方案6】:

        这可能会奏效。另一种选择是在字典中传递参数。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2011-07-06
          • 1970-01-01
          • 2016-04-13
          • 2013-10-01
          • 2013-02-02
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多