逐步提升程序质量的演变过程示例

如何编写高质量的程序呢? 在《Web服务端软件的的服务品质概要》阐述了程序的常见质量属性及实现策略方法,本文将通过一个 Python 实现的图片文件批量重命名工具来演示如何逐步提升程序质量。

图片文件批量重命名工具实现的功能是:将指定目录 /home/user/path/to/photos/(xxx.png,yyy.png) 下的图片批量重命名为 prefix0001.png, prefix0002.png, …

 

雏形

首先,可以编写出一个基本可用的程序 batchrename_basic.py 。这个程序并不完美,但是可以完成最初的任务。注意到 生成编号使用了闭包,这是为了将生成编号的过程抽离出来成为一个可复用的过程,而这个过程无法预知需要生成怎样的列表,因此每次仅返回一个编号;程序如下:

 

健壮性

健壮性体现了程序应对错误的能力。一个需要网络连接的 APP 在网络正常的情况下运行流畅,如果没有网络呢? 就必须告知用户先连接到网络才行。或者采用输入自动纠错。比如在搜索引擎里搜索 jquery, 不小心写成了 jqeury 。搜索引擎会提示是否需要搜索的是 jquery。在此例中,当路径不存在时,就会报错。

解决方法很简单: 将  names = os.listdir(dir_path) 抽离出来,写成一个函数并进行异常捕获,然后该行改写成 names = getDirFiles(dir_path):

 

可定制性

如果用户想指定路径和前缀,就必须在程序里修改并重新部署,显然是比较“僵硬”的。控制台程序通常要加上命令行参数,而实际应用则使用配置文件。下面通过使用 argparse 模块给该程序添加命令行参数,使之具备可定制性。添加一个 parseArgs 方法, 并修改 main 即可。注意到,使用了元组来清晰表达所希望返回的参数格式,便于主程序使用; 魔数均用字符串常量来表达,保证可维护性。

使用方式: $ python batchrename_robust_customized.py /home/lovesqcc/setupdir/scitools/pic/fuzhuang/fz2/ -p fz2  -m NUM 1 5

-p, -m 都是可选的。默认只需要指定目录路径。

 

可追踪性

可追踪性体现了程序运行过程的可知性和可监控性。记录程序运行中的关键状态和关键路径,也非常有利于出现错误时进行调试。在此例中,要将文件重命名的具体信息记录下来,简便起见,程序中只是打印一下:

安全性

安全性通常表达两层含义: 1. 程序绝对不能破坏用户的数据; 2. 程序必须防止其它程序破坏用户数据或窥探用户隐私。其中第一条是不可触犯的。当我们重复运行  $ python batchrename_robust_customized.py /home/lovesqcc/setupdir/scitools/pic/fuzhuang/fz2/ 时,会惊讶地发现,重命名后文件变少了!当运行足够次后,文件可能只剩下一个! 这是怎么回事呢? 运行若干次之后,截取一次结果如下:

稍作分析即可知道, Python os.rename 在 UnixSystem 上会默认覆盖已存在的文件,而 os.listdir 输出的结果是无序的! 解决方案也很简单:先将 os.listdir 输出的结果排序后再重命名,即要修改 getDirFiles:

 

可复用性

可复用性的关键是单一职责原则和接口定义正交。单一职责原则指一个函数或方法仅做一件小事,望名知义;接口定义正交是说每个函数、类接口定义的事情没有重叠,可以组合实现非常灵活的功能。如果程序具备较好的可复用性,那么,在扩展程序时也会获得益处,将改动影响局部化。在编写程序时应时时考虑抽离出可复用的过程和方法。可复用性也有助于编写更有效的单元测试。此例中正是遵循可复用性原则来编写程序,使得每次改动仅涉及一小部分。

 

可移植性

写程序是为了更好更广泛地使用。可移植性需要:1. 检测操作平台; 2. 将特定操作系统的符号和特定操作系统的行为替换成平台无关的。在本例中,要将路径分隔符 / 修改为 os.sep. Windows下的使用方式: D:>python batchrename.py -d F:picfuzhuangfz2 -p fz2 -m NUM 1 6

可扩展性

可扩展性体现了程序应对需求变化的能力。对于此例,可扩展性体现在四点: 1. 要对目录的子目录递归重命名; 2. 要对多个目录使用不同前缀进行批量重命名;3. 支持不同的编号生成方式;4. 对于非图片文件的批量重命名。 对于第一点,只需要修改 batchrename 方法即可,检测到如果是目录,则递归调用 batchrename ; 对于第二点,则需要修改命令行参数格式,增加 -d 参数,参数个数至少一个;修改 -p 参数,参数可为零到多个。如果给定目录数大于给定前缀,则使用最后一个前缀将前缀数补足;若给定目录数小于前缀数,则将从后数多余的前缀忽略。要修改 parseArgs 和 main;对于第三点,则要将生成编号的方式抽离成可复用的过程,使得每次仅返回一个编号;对于第四点,由于没有对文件类型做判断,因此也是适合于非图片文件的。最终的程序如下所示, 使用方式:

$ python batchrename_robust_customized_extended.py -d /home/lovesqcc/setupdir/scitools/pic/fuzhuang/fz2/ /home/lovesqcc/setupdir/scitools/pic/fuzhuang/fz1  -p fz2 fz1 -m NUM 1 5

 

性能成本

程序员有追求高效的强迫症。想象这是一个 web 服务, 性能成本通常体现在响应速度和吞吐量。响应速度是用户可感知的,影响到用户体验;吞吐量是用户不可感知的,影响到服务成本。此例中可以考虑百万个文件的重命名;影响效率的因素有两个: 1. 文件名排序时间; 2.  rename 系统调用时间。对于前者,使用快速排序,或者使用更精细的方法在 batchrename 函数中解决 os.rename 默认覆盖已存在文件的问题(这样会降低可维护性); 对于后者,如果编程平台或系统调用提供了更高效的批量重命名接口,则可批量调用该接口来完成任务。

结语

提高程序质量并非一蹴而就,而是可以通过渐进的方式来实现。当实现了一个基本可用的程序时,还处于一个起点,有必要问问自己:

1.  健壮性: 程序需要怎样的运行环境和输入参数? 如果运行环境不满足或输入参数不合法,程序该如何应对?

2.  可定制性: 程序有哪些参数或特性是可定制的? 切忌在代码里写死;

3.  可追踪性: 程序有哪些关键运行状态和关键运行路径? 使用 info 日志记录下来;

4.  安全性: 程序在何种情况下可能破坏用户的数据? 程序如何禁止非法程序破坏或窥探用户数据?

5.  可扩展性: 程序可能有哪些变化的潜在合理的需求?

6.  可复用性: 函数方法是否臃肿,可以从中抽离出可复用的子过程?

7.  可测试性: 关键函数和方法是否有充分的单元测试?

8.  性能成本: 响应速度是否在用户接受范围内?是否可以在不降低可维护性的前提下优化局部,提高整体吞吐量? 对于大数据量,程序是否可以应对? 程序的吞吐量极限是多少?

1 1 收藏 评论

可能感兴趣的话题



直接登录
跳到底部
返回顶部