易飞滔Todd | 次生进化

高可配置性软件开发

可配置性是软件工程实践中的重要问题之一,配置文件与可执行文件一起构成了软件运行时版本控制的要素,Visual Basic,Python、Lua等解释型语言,常以所谓“脚本”的形式出现,进一步提升了操作系统、游戏等大型软件的可配置性,而领域驱动设计在强调领域模型的同时,也带动了描述配置领域模型的领域特定语言的发展。在工业控制领域,软件的可配置性用组态的概念来描述;在游戏开发领域,一般用引擎和脚本的概念来描述。

1 基本原理

软件复杂性的根源在现实世界的复杂性,软件的复杂性超过人的理解能力时,会带来软件开发的困难乃至失败,好的软件工程方法论能给出现实世界的良好抽象模型,通过合理的抽象层次结构管理软件的复杂性,使人能从整体到细节分层次的把握软件。源自现实世界的复杂性并不会降低或消失,但却得到了约束。

高可配置性可在一定程度上解决快速需求变更问题,把握软件中不变的部分作为模型,将易变的部分通过配置文件等方式约束起来。

graph TB subgraph 软件 complexity2[复杂性]-->abstract[共通抽象] end subgraph 软件 complexity1[复杂性]-->complexity3[复杂性] end

2 构建方法

构建高可配置性软件,首先应当针对领域特点建立通用软件模型,然后针对模型使用配置技术。不同的配置加上模型最后构建了不同的软件实例。

graph TB subgraph 软件2 config2[软件配置2]-->abstract2[通用模型] end subgraph 软件1 config1[软件配置1]-->abstract1[通用模型] end

2.1 建模

建模问题是一个很复杂的问题,取决于设计时不同的场景,需要领域知识与设计经验,并且和领域专家密切合作。这方面有几本经典的书:《领域驱动设计》《实现领域驱动设计》

2.2 配置技术

2.2.1 配置文件

配置文件是使用最广泛也是最基本的一种配置技术,配置文件主要用于描述软件启动及运行时所需要的数据,一般不直接描述软件的运行逻辑,常见的配置文件格式包括ini、xml、json等,简单比较如下表所示:

格式 表达能力 可读性 典型应用场景 举例
ini 较弱,不可嵌套 最好 软件启动参数。 启动目录、ip地址。
xml 最强 最差 复杂对象的序列化。 数据帧结构与解析规则。
json 一般 一般 web API接口。 天气预报服务的接口。

2.2.2 脚本语言

一些大型的程序,往往通过结合多种编程语言来实现,一般通过编译型语言实现系统的相对固定的部分与运行效率关键部分,通过解释型语言来实现系统的易变部分。比如Linux操作系统用C实现了很多工具,而通过bash脚本对这些工具进行综合应用,大型游戏基于C++游戏引擎构造,而使用Lua脚本等描述游戏动作逻辑。

2.2.3 领域特定语言

领域特定语言介于一般的配置文件和程序代码之间,即针对某一特定领域,具有受限表达性的一种计算机程序设计语言。

领域特定语言又分为内部领域特定语言、外部领域特定语言、语言工作台等,其中内部领域特定语言是主要语言程序代码的一种特殊写法,不能算是配置,外部领域特定语言则设计新的语言来进行表达,仍可归为文本配置的范围。而语言工作台则主要提供可视化的配置方式或者成为可视化编程。

领域特定语言介于配置文件和脚本程序之间,提供了领域内较高的表达能力,也便于领域专家的理解,可加速专家知识向软件的生成。

针对不同模型的需要,以上3种配置技术在高可配置性软件开发均可应用,下表对三种配置技术的主要特点和典型应用场景做了比较。

配置技术 表达能力 可读性 典型应用场景 举例
配置文件 描述软件运行需要的数据,一般不直接描述软件运行逻辑。 取决于具体的配置文件格式。 软件启动参数。 启动目录、网络通信地址。
脚本语言 直接描述软件的运行逻辑。 仅适合程序员理解,不利于领域专家理解。 基本接口形式固定时,描述易变逻辑。 在C++构建的设备监控接口基础上用Python脚本描述监控流程。
领域特定语言 描述领域模型的配置,从而间接描述软件运行逻辑。 既适合程序员理解,也适合领域专家理解。 建立了认可度较高的领域模型,模型的配置丰富易变。 用“状态-反应”模型表现故障处理,建立领域特定语言描述具体的内容。

3 工程实践

如图所示,高可配置性软件开发实际改变了传统软件的开发流程,突出了软件配置在软件生命周期中的重要地位。下面将阐述高可配置性对传统软件开发各阶段带来的影响。

graph TB subgraph 高可配置性软件流程 requirements2[需求分析]-->construct2[软件主体构建] construct2-->config[软件配置] config-->test2[软件主体测试] config-->config_test[软件配置测试] test2-->maintain2[软件主体维护] config_test-->config_maintain[软件配置维护] end subgraph 一般软件流程 requirements1[需求分析]-->construct1[软件构建] construct1-->test1[软件测试] test1-->maintain1[软件维护] end

3.1 需求分析

在需求分析阶段,相比普通开发,高可配置性软件开发需要再重点分析以下问题:

  1. 采用高可配置性技术的必要性,这主要是由软件的适用范围决定的,即软件是否要具备可推广性,软件应对的需求是否易变。软件需要适配的场景越多,需求越易变,则必要性越大,如果软件只用于一个基本固定的场景,则没有必要使用配置。
  2. 区分模型需求与配置需求,在需求采集时,要采集软件在各种应用场景的需求,分析软件可能因推广带来的需求扩展,尽可能分析较多的案例,深入分析提取系统的不变性,抽象出系统的模型,而将系统的易变部分识别为配置需求。

3.2 软件构建

高可配置性软件开发的软件构建分为两个阶段:

  1. 软件主体构建:该阶段由程序员完成,高可配置性对架构设计要求较高,在使用面向对象范式时,需要深刻理解与应用面向对象的设计原则,大量应用各类构件型设计模式,以及反射、依赖注入等运行时关联技术;
  2. 软件配置阶段,该阶段可由程序员完成,也可由领域专家完成。由于配置成为软件的重要组成部分,所以对配置文件应当和对源代码一样严格管理,纳入版本管理系统,由于人工配置容易产生错误,所以应当尽量提供可视化的配置工具,限制用户随意修改配置。

3.3 软件测试

对应软件的两阶段开发,高可配置性软件的测试也分为两个部分:

  1. 软件主体测试,构造测试用例应当设计各种场景的配置,这些场景要考虑模型的边界情况,通过测试尽量消除软件主体的缺陷,减少软件推广应用时在配置后发现缺陷。

  2. 软件配置测试,完成配置测试才能在具体的场景应用,配置过程往往由领域专家而非程序员完成,因此最好对配置进行校验,并提供配置完成后的测试工具与测试方法细则,使得领域专家也能完成软件的测试。

3.4 软件维护

对应软件的两阶段开发,高可配置性软件的维护也分为两个部分:

  1. 软件主体维护,通用的功能升级通过对软件主体的升级完成,软件主体功能的修改应当保持配置的兼容,如果不得不改变配置的格式时,应当提供配置的升级转换功能;

  2. 软件配置维护,用户需求与应用场景发生了变化,而这些变化在软件主体预期之内时,可由领域专家修改配置完成,软件配置逐渐成为用户的重要资产。