设计模式在开发模拟器终端软件中的应用

2023-10-20 08:41樊建平宋众艳
火力与指挥控制 2023年7期
关键词:窗体设计模式观察者

樊建平,宋众艳,朱 胤,陈 涛,李 波

(北方自动控制技术研究所,太原 030006)

0 引言

QT 是一种C++应用程序开发框架,用于图形用户界面应用程序开发,拥有C++的性能、跨平台的特点、高质量的文档。软件开发人员在模拟器终端界面编程时,常用的方法是将窗体界面及界面中包含的所有控件放在同一个模块中,窗体类、各个控件对象之间互相可见,均可直接访问。这种方法特点是逻辑简单、实现容易、上手快、易学易用。

虽然上述方法具有简单易用的优点,但也存在明显的缺点。首先,界面上所有的控件编码均在同一个模块中实现,导致各个控件的代码交织在一起,控件、方法取名时容易冲突,调用时容易出错,修改和升级界面时,容易改错、不易维护。其次,由于所有控件都在同一个模块中,与控件相关联的逻辑功能的实现也被局限在同一个模块,这些逻辑功能结构松散、互相交织,导致这个界面模块无法体现出清晰的设计思路和工作逻辑。

本文提出一种终端界面编程方法,基于组合(composite)设计模式与观察者(observer)设计模式[1],可实现直接快速解决终端界面解耦和逻辑梳理问题。

1 组合与观察者设计模式

设计模式解决软件开发人员面临的一般性问题。设计模式具有复用解决方案[2]、确立通用术语、提高观察高度、软件更易修改维护等优点[3]。

1.1 组合模式

组合模式是设计模式中的一种[4],可以描述成部分与整体的层次结构,这样的结构可以用对象组合成树的形式来表示。单个对象和组合对象的使用,对用户来说具有一致性。组合模式结构[5]如图1 所示。

图1 组合模式结构Fig.1 Combination mode structure

组合模式对象结构如图2 所示。

图2 组合模式对象结构Fig.2 Object structure in combination mode

部件(component):声明组合中对象的接口并实现共同接口的缺省行为。

叶子(leaf):在组合中表示叶子节点对象并定义叶子对象行为。

组合(composite):定义部件的行为;含有子部件;在接口中实现与子部件相关的操作。

客户(client):通过部件的接口操纵组合部件的对象。

客户通过使用部件类接口,与组合结构中的对象进行交互。如果接收者是一个叶节点,便直接处理请求。如果接收者是一个组合,通常将请求发送给其子部件。在转发客户请求之前或之后可能执行一些辅助操作。

1.2 观察者模式

观察者模式是设计模式中的一种。观察者设计模式定义对象之间是一种一对多的相互依赖关系。通过一个类(观察者)在另一个类(主体)中注册通知的方式,当一个对象(主体)的状态发生改变时,所有依赖它的对象(观察者)都得到通知,并被自动更新,使概念上不相关的类之间可以相互通信[6]。观察者模式结构图如图3 所示。

图3 观察者模式结构Fig.3 Observer mode structure

目标(subject):维护一个包含所有观察者引用的列表。

观察者(observer):定义了一个更新接口,当目标状态发生改变时,观察者通过这个接口获得通知进行更新。

具体目标(concrete subject):当自身状态发生变化时,便利包含所有观察者引用的列表发出通知。

具体观察者(concrete observer):收到一个具体目标改变的通知后,通过重载父类观察者的更新接口,保存目标最新状态。

2 基于设计模式的分析步骤

2.1 QT 界面设计应用组合模式

2.1.1 QT 界面与组合模式

在QT 的界面窗体中,一个控件可以看作一个叶子节点,一组关联的控件可以看作一个部件。

当这个界面窗体作为子窗体嵌入到另一个界面窗体时,这个界面窗体又可以看作部件,被嵌入的窗体可以看作根部件。

依此类推,控件、窗体、子窗体互相组合与递归,形成不同层次的叶子节点与部件,构成了树形结构,即典型的组合模式树形描述[7]。

作为根节点的窗体部件接收到处理请求,会将请求发送给它的接收者,如果接收者是一个叶节点,便直接处理请求;如果接收者是一个部件,部件则将请求发送给它的子部件。这样传递请求,直到请求到达每一个叶节点。

2.1.2 QT 中组合模式的实现

QT 中组合模式的实现依赖QT 自定义窗体的“提升”功能。自定义窗体对应组合模式中的部件或叶子。“提升”流程如图4 所示。

图4 “提升”流程图Fig.4 “Promotion”flow chart

1)将一个窗体作为树的根节点

在这个窗体中,设计包含若干个部件(节点或叶子),其中每一个部件设计包含若干个子部件(节点或叶子)。

2)将自定义窗体作为树的节点

使用QT 控件Widget 在根节点窗体中进行“占位”。即将控件Widget 拖入界面设计好的位置。

在QT 中新建自定义窗体,使其大小与进行“占位”的Widget 控件匹配。

在自定义窗体中,根据实际需求设计放置若干QT 控件并设计接口,实现自定义窗口数据更新及数据表现。

3)通过“提升”功能实现树的构造

使用QT 编程环境Creator 的“提升”功能,将自定义窗体与“占位”的模块进行结合,自定义窗体替换掉了原来界面的“占位”控件,就可以通过这个自定义窗体类的指针,在根窗体中来访问这个窗体上的所有控件。

通过以上3 个步骤,利用QT 的“提升”功能,构成了组合模式结构,同时由于每个窗体部件是单独新建,因此,解耦了界面,降低了界面复杂性,使得各模块易编码易维护。

2.2 观察者模式的应用

根据2.1 节,对根节点窗体部件以及其包含的各个子部件应用观察者设计模式。根节点窗体部件作为主体即观察的目标,各子部件作为观察者即观察主体状态变化的对象。观察者设计模式应用流程如图5 所示。

图5 观察者模式应用流程Fig.5 Application process of observer mode

1)注册观察者。在根节点窗体部件中注册观察者,即维护一个包含所有观察者引用的列表。

2)当有更新数据时,通知观察者。当根节点窗体得到更新数据时,通知各个观察者,遍历包含有观察者引用的列表,各个观察者调用各自实现的更新接口,获取数据、更新状态。

3)使用推或拉模式更新观察者。观察者获取数据有两种方式,一种是拉模型,一种是推模型。所谓拉模式,指通过引用主体取得主体接口GetData 来获取所关注的数据;所谓推模式,指主体直接将观察者需要的数据作为接口Updata 参数,传给观察者。

通过以上3 个步骤,利用观察者模式,将主体与观察者解耦、降低各模块间耦合、各模块自动更新,梳理了工作逻辑。

3 实例分析

以某型模拟器炮长终端软件作为实例,来详述其技术方案。技术路线概要如图6 所示。

图6 技术路线Fig.6 Technology roadmap

3.1 界面分解

3.1.1 模块划分

按照实装界面形式与功能,进行界面的模块划分。

实装的炮长终端上部显示当前的工作状态,下部显示各个功能界面。按照界面形式与功能,进行界面的模块划分[8],可将界面初步划分为上下两个部分。

软件上部为显示状态的区域,下部分为人机交互的区域(包括信息输入和信息显示)。下部分人机交互区域包括多个子功能区域,因此,进行第2 层划分,每个子功能区域对应的划分为一个界面模块。这些子功能区域对应的界面以一组标签页进行组织。

这样界面划分共分两个层次,第1 个层次划分为上下两部分,第2 个层次,下部分再次划分为若干个部分。

3.1.2 界面设计

使用Qt Creator 开发环境,新建QT 工程,根据前述模块划分方案进行界面设计。

Step 1 使用QT 控件Widget 进行“占位”

1)新建Application 项目,选择Qt Widges Application,建立工程;

2)在工程中打开ui 界面,按照划分设计,在上部分拖动放置一个QT 容器类的控件Widget,作为状态模块;

3)在下半部分,拖动放置一个QT 容器类的控件Stacked Widge,作为功能模块;选中此控件,通过右键菜中插入5 个Widget,这5 个Widget 分别对应5 个功能界面。

Step 2 创建自定义窗体,根据前述的“占位”控件,新建窗体类与占位控件对应。

1)在Creator 中,通过工程中添加新文件,自定义一个派生于QWidget 类,完成后工程中就添加了新类和对应的窗体;

2)在窗体中按照实装界面进添加需要的控件;

3)按照上述步骤,分别对划分后的每一个模块建立自定义窗体、添加控件使与实装界面一致。

Step 3 提升窗体,将自定义窗体与“占位”的模块进行结合。

1)在ui 界面中,选中上半部分模块(占位控件),进行提升,提升后模块的类名变为自定义窗体的类名;

2)同样,对所有划分的区域(占位控件)进行提升操作。

这样就将自定义窗体替换掉了原来界面的“占位”控件。在占位控件所在的窗体中,就可以通过这个自定义窗体类的指针来访问自定义窗体中的所有控件。

3.2 工作逻辑梳理

将3.1 节界面分解得到的多个模块作为输入,采用观察者设计模式,对程序工作逻辑进行梳理。

3.2.1 应用观察者模式

对前述界面划分出的多个模块应用观察者模式,结合本实例,观察者模式结构图如图7 所示。

图7 观察者模式结构Fig.7 Observer mode structure

图7 中,主体(被观察的目标)是通信类(My-Communication);观察者是基类,定义虚函数作为更新数据接口;所有具体的观察者继承这个基类并各自实现更新数据的接口。

具体的观察者分别是:状态窗体、参数输入窗体、自检窗体、稳像窗体、装表窗体、校炮窗体。

观察者所观察的内容为通信类中得到的硬件实体按键、开关消息,以及来自火控仿真软件的系统状态与诸元信息。

3.2.2 观察者模式工作流程

Step 1 注册观察者:在通信类中注册各个观察者,即维护一个包含所有观察者引用的列表;

Step 2 当有更新数据时,通知观察者:通信类得到更新数据时,通知各个观察者,遍历包含有观察者引用的列表,各个观察者调用各自实现的更新接口,获取数据(实体按键、开关消息,以及来自火控仿真软件的系统状态与诸元信息);

Step 3 使用推或拉模式更新观察者:观察者获取数据有两种方式,一种是拉模型,另一种是推模型。

在本实例中,同时使用这两种方式来获取数据,如序列图8、下页图9 所示。

图8 数据拉模型Fig.8 Data pull model

图9 数据推模式Fig.9 Data push modes

在本实例中,采用拉模式获取火控仿真软件状态帧和诸元帧,采用推模式获取按键信息。

4 结论

综上所述,基于QT 编程环境,采用组合设计模式与观察者模式进行终端界面编程,是一种新的界面编程方法,其创新之处在于在采用组合设计模式之后,对树形的各个节点应用观察者模式、组合模式的结果作为观察者模式的输入,前者解决界面解耦,后者解决逻辑梳理,不仅使程序开发效率得到提升,而且程序的维护升级更为容易。利用本文方法,通过对某型模拟器炮长终端软件的实例,再次证明这一点。但本文中应用的组合模式、观察者模式都有一定的适用范围,解决的问题有一定的局限,如何应用其他的设计模式解决更多的问题,都有待进一步分析、解决。

猜你喜欢
窗体设计模式观察者
“1+1”作业设计模式的实践探索
三维协同设计模式下的航天项目管理实践与展望
试谈Access 2007数据库在林业档案管理中的应用
冷静而又理性的观察者——德国华人作家刘瑛访谈
交通机电工程设计模式创新探讨
互动式设计模式研究
巧设WPS窗体控件让表格填写更规范
观察者模式在Java 事件处理中的应用研究*
委托与事件在观察者模式中的应用*
火车匀速进站时鸣笛音调不变化吗?