答案: 考虑未来
我们之所以考虑未来,主要的原因就是需求的不稳定。因此,我们如果考虑未来可能发生的需求变化,就会不知觉的在架构设计中增加复杂的成分。这违背的简单的精神。但是,如果你不考虑可能出现的情况,那些和目前设计格格不入的改变,将会导致大量的返工。
还记得YAGNI吗?原则上,我们仍然坚持不要在现有的系统中为将来可能的情况进行设计。但是,我们必须思考,必须要为将来可能出现的情况做一些准备。其实,软件中了不起的接口的思想,不就是源于此吗?因此,思考未来,但等到需要时再实现。
变更案例有助于我们思考未来,变更案例就是你在将来可能要(或可能不要)满足的,但现在不需要满足的需求。当我们在做架构设计的时候,变更案例也将会成为设计的考虑因素之一,但它不可能成为进行决策的唯一考虑因素。很多的时候,我们沉迷于设计通用系统给我们带来的挑战之中,其实,我们所做的工作对用户而言是毫无意义的。
架构的稳定
架构简单化和架构的稳定性有什么关系吗?我们说,架构越简单,其稳定性就越好。理由很简单,1个拥有4个方法和3个属性的类,和1个拥有20个方法和30属性的类相比,哪一个更稳定?当然是前者。而架构最终都是要映射到代码级别上的,因此架构的简单将会带来架构的稳定。尽可能的让你的类小一些,尽可能的让你的方法短一些,尽可能的让类之间的关系少一些。这并不是我的忠告,很多的设计类的文章都是这么说的。在这个话题上,我们可以进一步的阅读同类的文章(关于 refactoring 的思考)。
辨正的简单
因此,对我们来说,简单的意义就是不要把未来的、或不需要实现的功能加入到目前的软件中,相应的架构设计也不需要考虑这些额外的需求,只要刚好能够满足当前的需求就好了。这就是简单的定义。可是在现实之中,总是有这样或者那样的原因,使得设计趋向复杂。一般来说,如果一个设计对团队而言是有价值的,那么,付出一定的成本来研究、验证、发展、文档化这个设计是有意义的。反之,如果一个设计没有很大的价值或是发展它的成本超过了其能够提供的价值,那就不需要去考虑这个设计。
价值对不同的团队来说具有不同的含义。有时候可能是时间,有时候可能是用户价值,有时候可能是为了团队的设计积累和代码重用,有时候是为了获得经验,有时候是为了研究出可重用的框架(FrameWork)。这些也可以称为目的,因此,你在设计架构时,请注意先确定好你的目的,对实现目的有帮助的事情才考虑。
Scott W.Ambler在他的文章中提到一个他亲身经历的故事,在软件开发的架构设计过程中,花了很多的时间来设计数据库到业务逻辑的映射架构,虽然这是一件任何开发人员都乐意专研的事情(因为它很酷)。但他不得不承认,对用户来说,这种设计先进的架构是没有太大的意义的,因为用户并不关心具体的技术。当看到这个故事的时候,我的触动很大。一个开发人员总是热衷于新奇的技术,但是如果这个新奇技术的成本由用户来承担,是不是合理呢?虽然新技术的采用能够为用户带来效益,但是没有人计算过效益背后的成本。就我开发过的项目而言,这个成本往往是大于效益的。这个问题可能并没有确定的答案,只能是见仁见智了。
简单并不等于实现简单
说到这里,如果大家有一个误解,认为一个简单的架构也一定是容易设计的,那就错了。简单的架构并不等于实现起来也简单。简单的架构需要设计者花费大量的心血,也要求设计者对技术有很深的造诣。在我们正在进行的一个项目中,一开始设计的基础架构在实现中被修改了几次,但每修改一次,代码量都减少一分,代码的可读性也就增强一分。从心理的角度上来说,对自己的架构进行不断的修改,确实是需要一定的勇气的。因为不论是设计还是代码,都是开发人员的心血。但跨出这一步是值得的。
下面的例子讨论了Java的IO设计,Java类库的设计应该来说是非常优秀的,但是仍然避免不了重新的修改。实际上,在软件开发领域,由于原先的设计失误而导致后来设计过于复杂的情况比比皆是(例如微软的OLE)。同样的,我们在设计软件的时候,也需要对设计进行不断的修改。能够实现复杂功能,同时自身又简单的设计并不是一件容易的事情。
例1.Java的IO系统
从Java的IO系统设计中,我们可以感受到简单设计的困难。
IO系统设计的困难性向来是公认的。Java的IO设计的一个目的就是使IO的使用简单化。在Java的1.0中,Java的IO系统主要是把IO系统分为输入输出两个大部分,并分别定义了抽象类InputStream和OutputStream。从这两个的抽象类出发,实现了一系列不同功能的输入输出类,同时,Java的IO系统还在输入输出中实现了FilterInputStream和FilterOutputStream的抽象类以及相关的一系列实现,从而把不同的功能的输入输出函数连接在一起,实现复杂的功能。这个实现其实是Decorator模式(由于没有看过源码和相关的资料,这里仅仅是根据功能和使用技巧推测,如果大家有不同的意见,欢迎来信讨论)。
因此,我们可以把多个对象叠加在一起,提供复杂的功能:
DataInpuStream in =
new DataInputStream(
new BufferedInputStream(
new FileInputStream("test.txt");
上面的代码使用了两个FilterInputStream:DataInpuStream和BufferedInputStream,以实现读数据和缓冲的功能,同时使用了一个InputStream:FileInputStream,从文件中读取流数据。虽然使用起来不是很方便,但是应该还是非常清晰的设计。
令设计混乱的是既不属于InputStream,也不属于OutputStream的类,例如RandomAccessFile,这正表明,由于功能的复杂化,使得原先基于输入输出分类的设计变得混乱,根据我们的经验,我们说设计需要Refactoring了。因此,在Java1.1中,IO系统被重新设计,采用了Reader和Writer位基础的设计,并增加了新的特性。但是目前的设计似乎更加混乱了,因为我们需要同时使用1.0和1.1两种不同的IO设计。
上一个:Flywheel:前进的马达在轰鸣
下一个:架构设计中的方法学(5)——简单设计(1)