分清设计的简单性与灵活性有时并不容易,让我们从一个简单的例子开始这一话题。假设我们需要编写一个函数,实现将“home.example.net!{sm=1}user@other.example.net”这样格式的字符串变为“home.example.net!user@other.example.net”,即去除其中花括号部分的内容。注意,花括号之前的网址可能会有多个。图1示例了第一种实现方法。
- void decoration_remove (char _user_name[])
- {
- char realm [NAME_MAX];
- char user [NAME_MAX];
-
- sscanf (_user_name, "%[^{]", realm);
- sscanf (_user_name, "%*[^}]%s", user);
- sprintf (_user_name, "%s%s", realm, &user[1]);
- return 0;
- }
图1
在该实现方法中,通过运用sscanf()函数,并在函数中使用正则表达式的方式分离出花括号前后的字符串,最后通过sprintf()函数将花括号的前后字符串拼接在一起放入传入参数_user_name中。尽管这一实现能达到目的,但作者在审查这一函数实现时指出:它的实现过于复杂。当作者提出这一观点时,有人主张:这样的实现具有更好的灵活性。
在作者看来,设计的简单性不只是体现在代码少,还包含概念的易理解性、知识点的使用尽可能少、程序执行效率更高等。上面的设计在代码行数上是简单,但它使用了没有必要的正则表达式知识点,且两个sscanf()函数的使用需要更多的处理器时间。
要明白一个实现是否真的具有灵活性,我们需要问自己“采用灵活性设计的依据是什么?”
灵活性不能全以自我感觉为依据,而是需要我们找到支撑灵活性的需求,如果需求不存在,那说明所谓的灵活性极有可能毫无意义,甚至会为我们的将来带来更大的不确定性。在上一实现中,使用正则表达式其实根本无法带来灵活性,如果将来有一天输入字符串的格式改变了,并不能因为使用了正则表达式而使得我们不需更改函数的实现。一旦实现的灵活性站不住脚,取而代之的是我们应当追求简单性,图2
示例了另一种实现。
- void decoration_remove (char _user_name[])
- {
- char *p_char = _user_name, *p_from, *p_to;
-
- while (*p_char != 0) {
- if (*p_char == '{') {
- p_from = p_char;
- }
- else if (*p_char == '}') {
- p_to = p_char ++;
- goto found;
- }
- p_char ++;
- }
- return;
-
- found:
- while (*p_to ++ != 0) {
- *p_from ++ = *p_to;
- }
- }
图2
在新的实现中,它通过遍历字符串的形式找到前后的花括号,并通过将花括号后面的字符向前移的方法将花括号部分的内容从原始字符串中删除。这一实现,不论是从时间冗余度或是空间冗余度都优于上一实现,且并没有使用正则表达式这一知识点。
尽管这里只以一个函数的实现为例解释简单性与灵活性,与软件的设计似乎存在很大的差距,但它并不影响我们思考简单性和灵活性。请别忘了,软件设计最终将在函数的实现上有所体现。
如果灵活性设计带来了简单性,那就不存在平衡问题。但是,当简单性与灵活性之间存在取舍时,如何在简单性与灵活性之间做出选择?可以通过问自己几个问题来帮助选择:
1) 灵活性设计的依据是否是来源于现有需求?不少采纳灵活性的设计者声称“如果将来变成了……的话,这种设计将更加的灵活”,出现这种陈述,往往意味着现有需求并没有要求这样的灵活性。在这种情形下,如果“那个如果”根本就不可能发生,那意味着我们并不需要所谓的灵活性。但是,当“那个如果”成立时,我们还得问第二个问题。
2) 灵活性设计的采纳与否,对于现在和将来的工作量是否会产生大的差异?如果现在和将来的工作量并不会因为灵活性设计的运用而产生大的差异,那我们还应选择简单性。反之应当选择灵活性。
读者或许意会到了,平衡简单性与灵活性的重点是为了尽可能做到简单性。具有简单性的设计除了更容易被理解和维护外,还意味着我们不致于过度设计。当出现过度设计时,它可能意味着浪费,也可能无意中又创造了更大的复杂度。
一个出色的设计,除非设计者已经具备了相同或相似的经验,否则很难一步到位。请不要相信每一次选择灵活性设计将使我们最终构建出一个出色的软件架构。相反,作者所持的观点是,高质量的设计是在追求简单性的过程中,在需求需要的情形下通过逐步的灵活性演进而创造的。
灵活性设计在不少情形下是一个优美的陷阱,要避免踏入这一陷阱,需要我们抵挡住“灵活性”的诱惑,而方法就是通过问自己前面提到的两个问题。另外,我们不应为了“显摆”而追求没有必要的灵活性,用直截了当的方式解决问题并不等同于“做事不够专业”。