设计的连续性

代码写的好,主要原因并不在于文字的输入水平。

而在于编写者在进行什么样的思考。

软件就是这样一点一滴逐步构建起来的。


微小的改变累积起来,它的影响就是可观的。

是编写者无时无刻进行的选择,造就了软件的演变。

编写者的作用,经常被低估。


好的设计,才能产生好的产品。

而不同于其他行业的是,

软件设计在设计之初是无法具体化的。


只有设计者才能实现他的设计。

在遇到意外情况时,

也只有设计者知道怎样调整,才能保持一致。


像行云和流水一样,

优美的设计体现在它遇到困难时的应变选择上,

而不体现在于它现阶段的实现方式上。


好的设计,也会遇到问题,

但只有设计者才知道,

怎样用准备好的方式来解决它。


设计和实现

水平较高的程序员,通常会被安排做设计。

而普通程序员,则要求去实现他们的设计。


这其实是非常危险的。


一方面,

设计的好坏,主要在面对具体问题时的选择上,

而这些选择,其实是由普通程序员来完成的。

设计者的水平虽高,却没有起到任何作用。


另一方面,

设计者,如果不设身处地的遭遇问题,

就不能提供合适的解决方案。

因此,他的设计很可能是偏离实际的。


最后,

高明的想法,是不容易被理解的。

普通程序员按着自己的理解,来编写代码。

会导致设计者遇到本来不会出现的问题。


因此,不同的人,可以负责不同的功能模块,

但是,对问题的思考和解决,必须由一个人来完成。


“我是这么想的,你来做吧。”

在软件行业中,通常是行不通的。


高水平的程序员,可以被安排去做复杂的模块。

普通程序员,可以去做简单的模块。

让他们互相隔离,尽量少的产生影响。


模块的自我保护——输入保护

软件提供的功能是分层次的,

虽然整个软件是交由目标用户来使用的,

但是,也可能软件的一部分是另一个部分的用户。


处理好层次之间的依赖性,

才能保证软件稳定。


作为功能的使用者来说,

最担心的就是,我们所依赖的模块发生变化了。


这种变化其实是不可避免的。

因为,不断演变,是软件的本质特征,

就算不变坏,变好也是变化的一种。


那该怎么办呢?


事实上,我们需要知道自己想要什么,

并且,用可替换的方式实现自己的想要的功能。


也就是说,我们并不依赖任何模块,

只依赖于我们的需要。

至于如何实现我们的需要,是灵活的,经得起变化的。


例如:

在web应用中,我们需要一个对话框。

并不关心它是如何实现的。


为了避免产生依赖,

我们首先封装自己的需要。

我们需要一个dialog,它可以弹出消息,dialog.pop(message)。


然后,我们可以寻找任何已有的组件来实现需要。

var dialog={

    pop:function(message){

        //调用已有组件,实现弹出消息功能

    }

};


这样的话,如果我们用来实现功能的组件发生了变化,

我们的需要并没有改变。

只需要用另一个组件来实现dialog.pop(message)即可。


可以这样做的原因就是,

需要通常是稳定的,

而实现方式,通常是不稳定的。


我们清楚的知道自己需要什么,

而别人提供的功能,我们并不清楚。


模块的自我保护——输出保护

好的模块,内部也是层次良好的。

它们不是为同一目的而服务的。

也像整个软件那样,模块的一部分为另一部分提供服务。


整个模块是变化中的,

其主要推动力在于,别人对模块的需求在不断变化。


任何外在表现的更改,总是会影响到内部结构。

但是影响的方式,却与模块内部层次的划分有关。


怎样保证在需求变更时,影响最小呢?


这一次,我们就不能再使用封装的办法了。

因为我们无法封装变动的需求。

这也是面向对象思想,经常误用的地方。


应该避免跟随别人的需要进行编程。

我们要有自己的功能集,

就像一个车厂要随时准备好零件一样。


我们打算用零件来拼装需求。

当需求变更后,我们只需要添加或更换零件即可。

中国的“活字印刷术”就是这种思想的应用。


例如:

在web应用中,我们需要提供一个对话框。

通常使用者会告诉我们,他们需要,dialog.pop(message)。


为了避免需求的变化过度影响我们,

我们将需要的功能划分为可拼装的零件。


myDialog.create(html);

myDialog.addClass(style);

myDialog.bindEvent(event);

myDialog.setMessage(message);


这些足够了,我们并不想创建多余的功能。

然后,我们拼装产品。


var dialog={

    pop:function(message){

        myDialog.create(html);

        myDialog.addClass(style);

        myDialog.bindEvent(event);

        myDialog.setMessage(message);

    }

};


这样的话,当需求发生变化时,myDialog不会整体受到影响,

增加或者更改部分功能以后,就可以制造新的产品了。


结语

对模块的保护,在输入端和输出端,是不同的。

不进行保护,或者不区分的进行保护,

都是危险的。


模块的设计和实现,必须由一个人来完成。

因为,模块是可替换的,

而设计和实现是相联系的。


本文提到的方法,只是一种解决方案。

其中包含的思想才是重要的。

可惜的是,它们包含的思想,也可能是过时的。

而发现这些思想的方法,是永不落伍的。