注册 登录  
 加关注
   显示下一条  |  关闭
温馨提示!由于新浪微博认证机制调整,您的新浪微博帐号绑定已过期,请重新绑定!立即重新绑定新浪微博》  |  关闭

LabATSS

关注自动测试软件

 
 
 

日志

 
 

LabVIEW面向对象编程—初窥门径(3):开发实践  

2018-03-27 18:16:37|  分类: LabVIEW |  标签: |举报 |字号 订阅

  下载LOFTER 我的照片书  |

引言:在去年年底左右(201711月底),由于单位业务调整,自己工作性质和内容发生较大的改变,由原先的计量参数日常运行检定改为软件维护与开发,遂开始着手对以往前同事开发完成的LabVIEW旧代码(直流电源校准/检定自动测试序列)进行重新梳理与维护,在此过程中开始边实践、边摸索的开发学习方式,应用面向对象技术(Object-Oriented Program)对旧代码进行了完全重新的改写,由于前段时间日常忙于开发工作,周末时间又要承担中年油腻男的上有老、下有小的生活重任,难得在春节及前后期间有一段相对空闲的时间,可以将近日的开发心得(设计思想、开发过程等)总结记录一下,以便日后回顾复习,并秉着互联共享的精神分享给大家,但是由于本人的工作内容为计量校准的测控行业,因此在实际代码开发实践时选用的案例为本行业的电学参数中的直流电源校准为样例,因此有其局限性,较为适合本行业或者是电子测试工程师们在碰到类似的代码开发情况下予以参考借鉴。

 

另外强烈建议大家在台式电脑上阅读该系列博客文章,缘由是:文章内容篇幅均较长,且图形化编程的截图显示效果要求大屏幕展示为宜,况且手机和平板等便携式设备易受干扰,导致碎片化式的阅读,从而恶化学习吸收效果;还请特别注意,该系列的博文并不讲述任何基础的LabVIEW面向对象概念与技术(基础部分内容将另行撰文编写),初窥门径系列重点是经典的设计原则在重构旧有面向过程代码的应用,实际发布面向对象工程代码中的填坑经验,因此并不适宜LabVIEW初学者学习, 如观看本文有云山雾里般的恶心、头晕、眼花等不适感,请立刻点击操作关闭页面,以免造成严重心理疾病,丧失了对LVOOP学习的浓厚兴趣!


目录:

LabVIEW面向对象编程—初窥门径(1):困惑





正文

本篇博文主要讲解在前文面向对象设计原则(SOLID)后,如何应用这些原则形成的LVOOPHALHardware Abstraction Layer)和MALMeasurement Abstraction Layer)的面向对象技术解决方案,并将这些方案实践到旧有工程代码中,进行直流电源的检定/校准项目重构。.

 

上篇博客文章中,我们提到面向对象设计中最为核心的原则就是DIP依赖倒置,即高层的策略要依赖于抽象层,底层的具体实现要继承扩展于抽象层,从而实现本质抽象,编程本身是软件开发人员对实际工作问题域的抽象建模并编码实现,面向对象编程实质上是提供一种良好的抽象映射工具,让编程人员能够针问题域事物抽象成逻辑概念及对象来进行封装,对象与对象之间的通信协作(公共接口编程)来完成问题的解决。大家可以参考一下《敏捷软件开发:原则、模式与实践(C#版)》(APPaP)图书中第20咖啡的启示的章节,用25页的篇幅讲解了一个简单的面向对象设计中的采用抽象和面向对象设计原则去管理依赖和分离关注点的实际案例。

LabVIEW面向对象编程—初窥门径(3):开发实践 - labats - LabATSS
 

回到本次重构直流电源检定/校准测试项目任务的本身,我们在动态调用框架(参见以前博文介绍(链接))下开发有大量的测试程序序列,测试序列子Vi通过VI服务器的动态调用函数被高层结构调用,以往的测试序列都是针对具体编程,开发难度较低,开发效率高,模块化图标操作与工程师的思维方式高度一致,容易被其他计量工程师快速学习掌握,大批量复复制粘贴代码完成率较高,但是也导致重复代码多,后期维护困难,不易扩展等弊端。而高层结构完成界面用户交互、数据报告的生成等公共基础功能。

 

重构主要是完成测试项目调用测试序列子Vi项目的完全面向对象技术的重构。之所以选择被测件为直流电源底层调用测试项目子VI来重构,主要是因为首先实际工作中直流电源现有的底层调用测试项目子Vi处于失控荒废的状态,急需恢复进行工程生产;其次,处理与硬件直接相关的抽象可以充分借鉴LVOOP社区的宝贵经验(如HALMAL的模仿和学习);第三,有目标客户对直流电源测试程序有较高的采购意向;最后,通过重构学习面向对象技术并积累设计经验形成二级调用子框架,并且兼顾对原先的已开发的大量测试Vi的兼容性,保护以往的资源投资。

 

本次重构完成后,希望实现下列计划需求目标:“满足针对抽象编程、复用概念,实现被测件型号动态灵活可扩展,支持测试标准可互换,测试项目与测试指标、测试点可以自定义、测量不确定度随测试类型(检定或校准)可增删。”因此在我的20171127日的工作笔记中我草拟了几个具体目标:

 

(配图20171128日的黑皮笔记本记录。)

 

实际上针对抽象编程、复用概念实际上是每个合格计量测试工程师都已经熟练掌握的能力,例如你学会了计量检定数字多用表的五功能或者是直流电源的项目检定,当有其他的类似的数字多用表或者直流电源送检时,你就具备了举一反三,照葫芦画瓢式地完成类似的任务,实际上日常依据的检定规程和校准规范就是非常典型的针对抽象描述,复用测试方法概念的典型应用(反过来说这也就是为什么规程规范类文件不好具体操作的原因),但是应用在程序开发编程上,难就难在抽象编程映射上,由于要把具体的问题域概念合理的抽象到软件对象上,这直接导致有一个巨大的转换鸿沟。而跨越鸿沟的方法就是先具体,后抽象,设计时多从上到下考虑,思考高层策略模块,不断迭代开发重构自己的代码。

 

以测量直流电源稳压负载效应实际开发为例,校准规范中明确了负载效应的定义和测量流程:即直流电源在空载和额定载荷下(带载)下输出电压(或电流)的变化性。基本的测试流程为,“先是用数字多用表测量空载输出电压,控制直流负载设备加载,等待稳定时间后进行载荷下输出电压测试。”上述的测试策略实际上就是直流电源负载效应测试的核心抽象,根据以往的代码可以看出真正的测试抽象分为测量行为状态动作:包含被测件状态详细动作及测量标准详细行为动作,最后通过具体的数据结构判定完成指标性数据判读。
LabVIEW面向对象编程—初窥门径(3):开发实践 - labats - LabATSS
 

让我们先从简单的设计类开始分析,检定/校准往往都需要性能测试指标这一数据结构来完成对测试过程中的测试点输出,以及测量结果的判读,以及最后的测量表格化或者图形化的结果输出,有数据、有行为符合我们对类概念的认知,内聚(SRP原则)的行为对必要的数据进行操作完成外界客户程序需求的服务。嗯,好像很简单。我们可以抽象封装这个类了LoadRegulationSpec.lvclass。形成的效果如下图所示:

LabVIEW面向对象编程—初窥门径(3):开发实践 - labats - LabATSS
 

我们有了一个良好的开始,从概念上说LoadRegulationSpec.lvclass类是一个具体的指标类,完成了对直流电源的负载效益指标和相关函数动作的封装。从程序的横切面观察,我们可以看到其他项目如电网效应、电压准确度、电流准确度等都有相应的指标类,因此应用抽象的概念,这些指标类是可以使用一个抽象类Specification.lvclass来角色替代,而这个抽象类需要完成概念角色的职责(包括与其它类的协作)有更新数据呈现结果,判断是否超差,获取当前测量值等抽象服务接口,从而使的上层测试策略依赖于抽象,底层的继承的具体指标类完成更为细节算法,这也是DIP依赖倒置原则的核心之所在。

LabVIEW面向对象编程—初窥门径(3):开发实践 - labats - LabATSS
 

通过反复不停的重构,我们将具体类中的公共功能函数上提到抽象类(Specification.lvclass),以及协调抽象指标类与协作类的通信接口数据抽象,形成我们第一个可用的类层次结构。抽象指标概念类由于要实现指标类角色的概念。每个具体的指标类型完成相对应的指标判读算法,数据结果集(DataSet)动态更新的功能,这个功能就要由LVOOP中的面向对象动态分配功能来支持了。

 

另外根据我们的重构目标计划,测试点和数据指标能够自定义,也就是说需要动态填充类中的指标建模数据,以往我们是直接的具体性能指标数字写死在被调用的子程序Vi当中,现在要动态填充因此需要将变化的部分抽取出来放到子程序Vi的外部去,单独存放,使用时读取填充类数据结构,很明显,我们需要一种存储机制来存放与具体型号相关联的的差异性指标数据。

 

存储方式一般有两种方式,第一存放至关系数据库中,依据具体型号查询获取;第二种为存储到以段、键、值形式的配置文件(ini)中,为简洁有效,便于离线测控计算机的维护,我们采取的是第二种方式即存储到配置文件中。

LabVIEW面向对象编程—初窥门径(3):开发实践 - labats - LabATSS
 

从配置文件初始化或者实例化类的私有数据,看上去非常诱惑人去把读取文件的功能职责函数合并到LoadRegulationSpec.lvclass类中,或者是其上层的抽象指标类中,但是请考虑SRP单一职责原则,使用类的数据和生成类的数据功能堆放在一起,形成两个不同维度的变化曲线,如果以后我们的存储方式改变为外部数据库(SQL Server)存放项目指标文件,将会使得真个指标类发生变异,因此生成指标类应该是另外的单独的一组类层次完成生成功能,而具体到LoadRegulationSpec对应的生成类为LoadRegulationSpecStrategy.lvclass

LabVIEW面向对象编程—初窥门径(3):开发实践 - labats - LabATSS
 

根据具体不同项目技术指标我们纷纷组建对应的指标生成类,完成从INI配置文件中读取相应的段、键,得到差异值,再实例化具体的对应指标类。实际上这就是面向对象设计中的抽象工厂模式,我们通过应用设计原则SRPDIP,使用和创建的分离(在后面的博文还要重点讲解)重构得到了一个设计模式,因此可见是设计模式是我们结的果,但是请注意我们开始时并不是瞄着这设计模式倒果为因式来开发的,忘掉设计模式去开发,应用设计原则反复迭代重构代码,最终你会得到好多设计模式的果。

 

完成了指标类和指标生成类的设计,下一步就是非常重要的测试项目行为操作本身的面向对象设计,一个项目的测试动作行为组成可以从不同的切面看待,就有不同的分类方式,从设备组成上可以分为被测仪器和测试标准,从测试流程(或者更为准确的说是测试状态)分可以分为有测试初始化、测试基础设置准备、测试循环、超差处理、异常处理以及测试结束。

LabVIEW面向对象编程—初窥门径(3):开发实践 - labats - LabATSS
 

很明显的可以看到,测试状态的变化可以封装为单一的测试操作类(Operation.lvclass),并且根据实际测试状态对应着实际具体测试项目的抽象动作。这里我们会同样应用依赖于抽象的DIP原则,从抽象类Operation.lvclass派生出具体的直流电源测试项目类。

LabVIEW面向对象编程—初窥门径(3):开发实践 - labats - LabATSS
 

而每个具体对应的测试项目类都有自己对测试状态动作的细节操作,因而形成子类具体化、差异化并且符合LSP里氏替换原则,另外一方面:并实现具体依赖于抽象的DIP重要的组成原则,进一步我们又形成了测试操作类的类族,即继承于抽象类(Operation)的一组具体测试项目类。举例说明,下图为前面博文演示过的直流电源的负载效应所重构以后的面向对象设计的测量行为的抽象概念方案。

LabVIEW面向对象编程—初窥门径(3):开发实践 - labats - LabATSS
 

继续完成前述的分解设计,完成对测试类的组成设备的设计抽象,并且再次应用依赖于抽象(DIP原则),完成对直流电源类、数字多用表类、直流电子负载类的抽象,并且由于这些硬件设备操作类本身的角色就是相互独立的,因此也就完全符合SRP单一职责设计原则。

LabVIEW面向对象编程—初窥门径(3):开发实践 - labats - LabATSS

 

这里就可以很好的对应应用前面博客文章中所提到的HAL(Hardware Abstraction Layer)MAL(Measurement Abstraction Layer)概念,其中HAL用来对被测件和测量标准设备进行概念封装,而MAL可以对不同测测试项目(测量行为如直流电源的负载效应)进行概念封装,关键核心抽象中的测试动作类(Operation.lvclass)与抽象指标概念类(Specification.lvclass)之间通过抽象数据公共接口来进行通信协作完成问题域的关键映射。这样就把针对抽象编程,高层概念复用得到良好的设计应用,详细见下图所示,指标抽象类负责提供测试数据点和进行结果集的更新,而测试操作类完成相应的硬件操作,返回测试结果。

LabVIEW面向对象编程—初窥门径(3):开发实践 - labats - LabATSS

LabVIEW面向对象编程—初窥门径(3):开发实践 - labats - LabATSS
 
 

有了以上的抽象角色概念,测试任务就变成了抽象概念之间的通信互动,而交互的通信接口的数据传输抽象,这里使用的是变体数据,在考虑使用变体Variant还是DTOData Transfers Object)数据传输对象时,当时的决策是考虑降低使用类的数量,不想过度设计,并且由于DTO本身是对象的贫血模型,即只有数据操作访问设置的操作,而没有真正的功能算法,因此从结构上来看与使用结构体(Cluster)并无大的差异,因此我选择了变体代表抽象数据结构,进而使用更为细节的结构体(Cluster)来表示要传输的数据结构。而实际上正确的选择还是应该使用数据传输类(DTO),原因其实在这篇(链接)翻译PPT文章已经表明了:变体通常应用在当需要不同的数据类型传输在单一连线中,无论何时你感觉需要使用变体的时候,都可以考虑使用类层次来进行替代。 原因是类在编辑时依然能够确保是强制严格数据类型,从而保证没有运行时错误,而变体则不然,由此增加了运行时错误的可能性。,即使用类能够在开发过程中进行保证类型安全,可以降低在运行时类型不匹配的问题。因此,这部分代码以后还要重新重构成类层次;而实际上目前开发中的代码还完全谈不上不多么正确合理,但是一直在持续改进!所以要想达到一个较好满设计方案需要耗费大量时间和资源的艰苦劳动。


通过不断的设计与开发,我们拥有了上层抽象复用的概念以及抽象协作接口,从而很好的实现了上层模块复用,但是在测试过程中,我们还是需要将这些概念抽象类实例化具体类完成细节操作动作,并且实例化技术(创建生成)也是面向对象提供扩展性的核心之所在,后一篇博文我们将重点介绍创建(实例化)与使用的分离,以及使用面向对象技术实现扩展性的插件结构。

 

参考文件
Using OOP in Measurement Systems》,National Instruments Corporation2014 NIDay


  评论这张
 
阅读(82)| 评论(0)
推荐 转载

历史上的今天

在LOFTER的更多文章

评论

<#--最新日志,群博日志--> <#--推荐日志--> <#--引用记录--> <#--博主推荐--> <#--随机阅读--> <#--首页推荐--> <#--历史上的今天--> <#--被推荐日志--> <#--上一篇,下一篇--> <#-- 热度 --> <#-- 网易新闻广告 --> <#--右边模块结构--> <#--评论模块结构--> <#--引用模块结构--> <#--博主发起的投票-->
 
 
 
 
 
 
 
 
 
 
 
 
 
 

页脚

网易公司版权所有 ©1997-2018