为解决工作中一些繁琐的问题, 写了一个GUI程序, 操作界面是这个样子的

把需求变化带来的代码修改成本降至最低的一种方法_图片

这个程序的实现起来并不是非常的繁琐, 但在界面的交互操作上, 也不仅仅只是展示数据。 如上面图片所见,列表中的每一条记录每一个数据项都需要可以填写和选择; 需要添加和删除记录;还需要调整记录的位置;向上移动、向下移动;要实现这些操作, 控制UI的程序其实挺复杂的。

 

我哼哧哼哧的把这个程序写完, 拿去给同事们演示使用方法, 同事们给我提出了不少的建议。 其中的一条是:把界面分割成上下两部份的方式替代列表中类型字段的选择, 以简化交互操作, 也就是说简化过后, 程序的操作界面要变成下面这个样子

把需求变化带来的代码修改成本降至最低的一种方法_成本_02

以写代码为生的同学肯定知道, 需求更改后的实现并不是一件愉快的事, 虽然给我提建议的同事并不是需求方, 但是他们的一些意见我不得不考虑, 我也不得不承认, 在软件操作的便利性上, 同事们给出的意见确实要比原来强上不少。 但是假如真的要以这种方式去修改程序, 大家可以设身处地的想想, 假如这个程序是由你开发的, 要做这样的修改, 会不会是一件很费劲的事情? 大量的代码逻辑变动或者以复制代码的方式让界面上的两个列表的UI交互操作互不冲突并且不影响结果的正确性是不可避免的,甚至于在极端情况下, 会让整个程序的结构产生变动也未可知。 当同事们建议的声音钻入我的耳朵的第一瞬间, 我就觉得这是一件不可能实现的事情, 第一反应就是立马反驳, 并表示这是一项无法完成的工作。

 

事后,我静下心来思考这个事情。 首先, 同事们的建议是完全合理的, 除了程序的修改难度问题,我找不到合适的理由拒绝; 其次, 我回忆我写这个程序时代码逻辑的组织方式,我发现假如我要把程序修改的符合像同事们建议的那样似乎也并不需要费多少功夫, 而且可以说是非常非常的简单, 简单到不可思议。 我照着我脑海中生成的方案去做, 只花了15分钟左右时间就完成了任务, 实现了指定的效果, 而且只修改了五六行核心业务逻辑代码, 界面和操作的变动与工作量以及代码的修改完全不成正比,这让我自己也觉得很惊。 修改的过程中我大致做了下面这些事情

 

界面部份的改动

 

1.      调整界面中各个组件的尺寸, 腾出一块空白的区域来放第二个列表

2.      把第一个列表的xaml代码复制一份到刚刚腾出来的位置, 这段xaml代码是一个ListView控件,所以需要给它命一个新的名称

3.      把界面右上角「添加一项」按扭也复制一份, 放在第二个ListView的上方位置, 并绑定一个新事件

 

程序部份的改动

 

  1.   为新的ListView绑定一个数据源

把需求变化带来的代码修改成本降至最低的一种方法_成本_03

2.  为新的「添加一项」绑定事件代码

把需求变化带来的代码修改成本降至最低的一种方法_图片_04

然后, 大功告成, 就这么简单的把这事给办了

 

有同学可以会提出疑问:“不说别的, 就说第二个列表的删除、上移、下移这三项功能的事件代码写在哪了? 你这是当我们是没写过代码的小白来忽悠吗?”, 事实上, 这些代码是有的, 都是复用前一个列表的事件代码。“但为什么针对前一个列表的事件代码毫无变化的过渡到新的ListView上使用呢?这不符合常规编程逻辑”,这其实跟我程序代码的设计方式有关

 

大家看到程序的界面中有许多界面交互操作的功能,如添加、删除、上移、上移, 只要鼠标点击在这些按扭之上, 界面就会立刻发生变化, 这势必需要通过程序去控制界面元素。 这个程序是用C#和XAML开发了, 但考虑到受众问题, 我用JavaScript和html举个例子, 假如我们需要移除一个表格中的一项, 那么我们肯定要通过文档对象模型去操控这张html表格,比如说通过这样的方式去移除

 

var ele = document.getElementById("表格行ID");

ele.parentNode.removeChild(ele);

 

为列表添加一也是同样的道理, 上移下移列表项也是一样, 只是实现起来更加的困难复杂, 但这都是常规的实现思路。

 

然而,我却不是以这种方式去实现这个WPF GUI程序的。 再举个例子, 在我们开发Web应用程序时以列表的方式展示数据最常见不过,当我们要删除某一条数据时, 不使用ajax进行无刷新删除的做法是,先删除数据,再刷新页面,那条需要删除的数据就被去除掉了, 数据库和界面, 双重的。 这种方法的优点就是逻辑简单, 以刷新页面替代JavaScript操作DOM来进行界面更新; 缺点就是体验差,没有办法做到无刷新更新页面。 对页面的其它操作也可以相同的方式更新UI, 将记录插入数据库后刷新页面,界面上显示的数据也会随之增加;修改数据库中记录的排序号码,刷新页面后界上对应的数据项也会转移到相应的位置;

 

我正是借用了这种浏览器/服务器架构的程序设计思路,才把问题简单化,省略了各种动态更新UI的程序操作, 对UI的更新只在ListView绑定数据的时候进行了。 我设计这个的核心思路大致如下

 

1.  新建一个列表数据结构, 用来存放显示在ListView控件中的内容

2.  执行添加操作时往这个列表结构中插入一条数据, 然后重新把数据绑定至ListView, 使其重新渲染界面。 所有添加操作都是以这种方式执行, 先更新数据结构, 再渲染ListView

3.  删除操作与添加操作相似, 先将数据项从列表数据结构中删除, 再让ListView根据数据源重绘UI

4.  其它对UI的操作亦都是如此

 

将所有原本需要对UI进行的操作都转移至对数据进行操作, 再根据被操作后的数据结果重绘UI, 这样做的好处是代码的逻辑变的清晰简单了,除了将数据映射成界面的时候需要关注UI相关的逻辑, 其它时候只关心数据就可以了, 操纵数据结构的难度显然是低于操作界面元素的。缺点就是每一次交互操作导致数据产生变化后, 都需要完全重绘UI,影响用户体验。对于Web应用程序这种影响很明显,因为需要执行一次http请求,在浏览器内刷新页面。 而对于windows GUI应用程序,这种体验上的差距用肉眼几乎难以观察的到, 数据是从内存中读取的, 没有任何网络开销;而重绘界面的时间只需要几毫秒甚至更少,因此完全没有理由去关注这些根本不会影响到软件使用的问题, 我们应该关注的是如何简化代码,如何提升软件可用性等实质性的问题。

 

我的程序以这种设计思路实现, 在应用同事们提升出建议修改程序时,概括来说我就做了两件事

 

1.  修改界面, 多加了一个ListView控件, 两个控件的结构完全一样

2.  把原来的一大份数据,拆成了两份,分别绑定至两个ListView

把需求变化带来的代码修改成本降至最低的一种方法_成本_05

修改成

把需求变化带来的代码修改成本降至最低的一种方法_图片_06

就这么任性的搞定了

 

很多时候我们总是抱怨需求的变化导致我们工作量加大, 每当听到需求有变化需要把程序大改特改的消息时就像听到了自己的女朋友跟别的男人跑了一样激动, 认为一切都需要推倒重来, 所有的努力的付出都浪费了。 然而, 事实上, 代码逻辑组织的有足够的技巧, 完全可以将需求变化带来的对代码改动的影响降至最低。每一个专业的程序员都应该具备“应对变化”的能力。我们的时间是有限且宝贵的, 写出愚蠢的需要花大量时间去维护以及无法应对变化的代码是一种浪费时间、浪费生命的慢性自杀,所以,写代码时注意技巧,永远不会错