【LabVIEW面向对象闲谈】#3 – 接口 |一个真实世界的例子

在[上一篇文章]中,我们探讨了接口的定义及其相关的好处,既包括一般性的,也涉及LabVIEW领域的应用。本文通过举例说明接口和类的实际应用,讲述了所见的概念。这篇文章的目标是提供更多见解和工具,帮助你使用与真实问题的接口。

要求

假设我们需要开发一个管理空气质量监测站的应用程序。要求包括:

  • 该应用管理一个空气质量监测站(主站),传感器和另一个外围站连接其上。
  • 应用会读取PM10和一氧化碳一氧化碳浓度的值,并按特定速率计算。
  • 该应用程序读取连接主监控站的两个传感器。这些传感器分别是PM10颗粒传感器和一氧化碳一氧化碳传感器。
  • 应用程序读取从外围监测站获取的PM10和一氧化碳值。
  • 应用程序会在系统控制台中记录所有获取的数据。
  • 该应用也开放支持其他类型传感器和其他日志模式等扩展。
设计

既然我们已经有了应用需求,接下来我们进入类和接口的设计。

首先,我们定义用于包含所有与测量相关数据的测量类别:值、计量单位和测量数据来源。

测量课。

现在我们来谈谈日志记录部分。
要求我们将获得的数据记录在系统控制台中。因此,我们创建了定义LogData方法的DataLogger接口和实现该接口的ConsoleLogger类。DataLogger接口的使用使该应用能够使用其他日志模式。举例来说,我们还定义了 SQLDatabaseFileLogger 类。两者都实现了DataLogger接口。然后
我们定义了DataLoggerManager类,负责管理数据日志。DataLoggerManager 类的属性类型为 DataLogger(合成关系)。因此,由于DataLoggerManager类依赖于DataLogger接口而非具体类,可以通过注入任何满足DataLogger接口的对象来更改日志模式。
以下是DataLogger接口的UML图、实现该接口的类以及DataLoggerManager类。

UML图,展示与日志相关的类和接口。

接下来我们通过定义与传感器相关的架构进行定义。
我们定义了基础类传感器,其属性包括名称(传感器名称)、序列号最后校准日期(LastCalibration)以及校等方法。请注意,除Name外,传感器类成员不满足上述要求。相反,它们使这个例子更贴近现实世界的应用。随后
,我们通过两个具体类别来表示PM10颗粒和一氧化碳一氧化碳传感器,这些类别均源自Sensor。这两个类别分别是PM10SensorCOSensor类别。传感器类别中未定义这两个传感器获取空气中PM10和一氧化碳浓度的能力。
取而代之的是,定义了两个专用接口:

  • CanMeasurePM10,定义了GetPM10Value方法。
  • CanMeasureCO,定义了GetCOValue方法。

利用上述两个接口,模拟这两种传感器的两个类只实现它们实际满足的接口。PM10Sensor类实际上实现了CanMeasurePM10接口,COSensor类实现了CanMeasureCO接口。
采用这种方法,如果我们添加一种能够同时获取PM10和CO浓度的新型传感器,我们将创建一个新类实现这两个接口。此外,如果未来我们获得新的数据类型,我们会创建一个新的专用接口,而不涉及现有代码。这满足了上述要求之一。
下面是建模传感器及其相关接口类别的UML图。

UML的传感器类和界面图。

我们通过对监控站建模,完成了类和接口的设计。我们再次提供了一个基础类(AirMonitoringStation)和两个衍生类:MainStationPeripheralStation
AirMonitoringStation定义了属性名称(代表站点名称)和位置(表示监测站的位置)。再次注意,位置属性并非满足要求的必要条件,但使当前示例更具体。
根据要求,外围站能够向主站提供PM10和一氧化碳浓度值。因此,从数据采集的角度来看,外围站与两个PM10和CO传感器起到相同的作用。因此,为了简化主站的工作,我们利用了两个传感器使用的相同接口。由于外设站同时提供PM10和一氧化碳浓度值,PeripheralStation类实现了两个接口:CanMeasurePM10CanMeasureCO
主机类负责从其处理的数据源(即传感器和外围站)读取PM10和CO值。通过接口的使用,MainStation类不依赖具体类,而是依赖于接口本身,从而相互解耦具体类,使应用更灵活、更易维护和可测试。
因此,MainStation 类的属性中包含两个列表,分别是 CanMeasurePM10 类型和 CanMeasureCO 类型。此外,该类还有两种专用方法用于读取所有数据源的PM10和CO值。这些方法分别是*:GetPM10Values* 和 GetCOValues
以下是建模监控站和相关接口的类的UML图。

UML关于空气监测站的类别和细节图。

最后,下面的UML图展示了所有定义的类和接口及其关系。为了方便阅读,图中未报告测量类。

所有类和接口的UML图

补充

我们一步步实现上述设计的类和接口。特别是,我报告了LabVIEW和C#版本的代码片段和截图。

让我们从测量课开始:

公共类测量
{
公共 双重价值 { get; set; }
公共字符串单元 { get; set; }
public string Source { get; set; }

公共测量(双倍值,字符串单位,字符串源){

this.价值=价值;
就是这样。单位=单位;
就是这样。来源 = 来源;
}
}

LabVIEW |测量课。

现在我们进入与传感器管理相关的类别(SensorPM10SensorCOSensor类别)及其相关接口(CanMeasurePM10CanMeasureCO)。
两个接口 CanMeasurePM10CanMeasureCO 分别定义了 GetPM10ValueGetCOValue 方法。两种方法都返回测量类型的对象:

公共接口 CanMeasurePM10
{
public Measurement GetPM10Value();
}

public interface CanMeasureCO
{
public Measurement GetCOValue();
}

LabVIEW |CanMeasurePM10和CanMeasureCO接口。

按照设计,传感器类定义了名称序列号LastCalibration属性以及校准方法:

public class Sensor
{
public string Name { get; set; }
公钥字符串 SerialNumber { get; set; }
public DateTime LastCalibration { get; set; }

public Sensor(字符串序列号,字符串名)
{
this.序列号 = 序列号;
就是这样。姓名=名称;
}

公共虚拟虚空 Calibrate()
{
Calibrate
this.LastCalibration = DateTime。现在;
}
}

LabVIEW |传感器级。

我们总结了关于传感器部分的实现,衍生出两个类别PM10SensorCOSensor。两者分别实现了CanMeasurePM10CanMeasureCO接口:

公共类别 PM10Sensor : Sensor, CanMeasurePM10
{
public PM10Sensor(字符串序列号, 字符串名) :
base(serialNumber, name) { }

public Measurement GetPM10Value()
{
返回新的 Measurement(new Random()。Next(0,100),“ug/m3”,这个。姓名);
}
} 公共

类 COSensor: Sensor, CanMeasureCO
{
public COSensor(string serialNumber, string name) :
base(serialNumber, name) { }

public Measurement GetCOValue()
{
返回 new Measurement(new Random().Next(0, 60),“mg/m3”,这个。名称);
}
}

LabVIEW |PM10传感器类。

LabVIEW |COSensor级别。

现在我们转向AirMonitoringStation类及其衍生类的实现。
抽象类 AirMonitoringStation位置名称数据的一个简单容器:

public abstract 类 AirMonitoringStation
{
public string Location { get; set; }
公共字符串 StationName { get; set; }

protected AirMonitoringStation(字符串位置,字符串站点名称)
{
this.地点 = 地点;
就是这样。车站名称 = 车站名称;
}
}

LabVIEW |AirMonitoringStation课程。

按照设计,我们从 AirMonitoringStation 衍生出两个类别:MainStationPeripheralStation
PeripheralStation类除了源自AirMonitoringStation外,还实现了两个定义的接口,也用于传感器(CanMeasurePM10CanMeasureCO):

公共类外围站 : AirMonitoringStation, CanMeasurePM10, CanMeasureCO
{
public PeripheralStation(字符串位置,字符串站点Name) :
base(location, stationName) { }

public Measurement GetCOValue()
{
返回新的测量(new Random().Next(0, 60),“mg/m3”,这个。车站名称);
}

公共测量 GetPM10Value()
{
返回新的测量(new Random().Next(0, 100),“ug/m3”,这个。车站名称);
}
}

LabVIEW |PeripheralStation级别。

MainStation类则更为复杂。根据设计定义,该类依赖于 CanMeasurePM10CanMeasureCO 接口,分别有两种不同的方法读取 PM10 和 CO:

公共类别 主站 : AirMonitoringStation
{
公共 MainStation(字符串位置,串站点名称) : base(位置,站点名称) { }
公共列表?COSources { get; set; }
公开列表?PM10来源 { get; set; }
公开名单<测量>?GetPM10Values()
{
如果(这个)。PM10Sources 空的)
{
返回 null;
}
var measurementList = new List();
foreach( var项。PM10Sources)
{
measurementList.添加(项)。GetPM10Value());
}
返回 measurementList;
}
公开名单<测量指标>?GetCOValues()
{
如果(这个。COSources 空的)
{
返回空;
}
var measurementList = new List();
foreach( var项。COSources)
{
measurementList.添加(项)。GetCOValue());
}
返回 measurementList;
}
}

LabVIEW |主站级。

我们最终进入日志部分,创建定义LogData方法的DataLogger接口。LogData 方法接收日期字符串参数作为输入:

public interface DataLogger
{
public void LogData(string data);
}

LabVIEW |DataLogger接口。

DataLogger接口由负责在系统控制台中写入数据的ConsoleLogger类实现。注意,对于 LabVIEW 版本,由于控制台不原生支持,控制台本身是通过写入字符串指示来模拟的。

public class ConsoleLogger : DataLogger
{
public void LogData(string data)
{
Console.WriteLine($“新记录:{data}”);}
}
}

LabVIEW |ConsoleLogger 类。

然后我们有LoggerManager类来处理日志操作。该类不依赖于具体类,而是依赖于DataLogger接口。通过构造子(或LabVIEW的Create方法),我们注入具体对象(我们这里是ConsoleLogger对象)。

public class DataLoggerManager
{
private DataLogger logger;

public DataLoggerManager(DataLogger logger)
{
this.logger = logger;
}

public void LogData(string data)
{
this.日志记录器。LogData(数据));
}
}

LabVIEW |LoggerManager课程。

让我们以创建一个主应用程序来结束这个示例,将上述所有类和接口汇聚在一起。
该程序初始化了 MainStationPeripheralStationPM10SensorCOSensorConsoleLogger 类型的对象。后者随后被注入类型为 LoggerManager 的对象中。
MainStation对象的PM10源代码列表包括PM10SensorPeripheralStation对象。同样,CO数据源列表由COSensor传感器和PeripheralStation对象组成。
最后,程序定期查询PM10和CO源,并通过LoggerManager对象记录数据。

内部类 Program
{
static void Main(string args)
{
站点
初始化 var mainStation = new MainStation(位置:“location 1”, “Main Station”);
var peripheralStation = PeripheralStation(位置:“位置2”,“外围站点1”);
传感器
初始化 var myPM10Sensor = PM10Sensor(序列号:“1234”,“PM10 传感器 1”);
var myCOSensor = new COSensor(序列号:“ABCD”,“CO传感器1”);
LoggerManager 的初始化 obj
var loggerManager = new DataLoggerManager(new ConsoleLogger());
将PM10和CO源分配到主站
主站。PM10Sources = 列表 { myPM10Sensor, peripheralStation };
主站。COSources = List { myCOSensor, peripheralStation };
虽然(!控制台。KeyAvailable)
{
返回并记录每个PM10值
(主站的var测量)。GetPM10Values())
{
loggerManager.LogData($“{measurement.来源} - {测量。价值} {测量。单位}“);

} 返回并记录每个CO值
(主站变量测量)。 GetCOValues())
{
loggerManager.LogData($“{measurement.来源} - {测量。价值} {测量。单位}“);

} 线索。睡眠(1000));
}
}
}

LabVIEW |主要 应用。

以下是工作示例:

|-64x-36

C# |正在运行应用程序。

LabVIEW |正在运行应用程序。

这个例子结束了关于接口及其用途的讨论。感谢您花时间阅读这篇文章。
下面你会看到直接下载LabVIEW项目(2023版)的链接,以及一些关于接口使用的其他链接(包括但不限于LabVIEW)。
下次见!

下载ZIP LabVIEW项目