winston 发表于 2012-3-20 23:28:50

通过读代码学习软件架构设计

  在客户端软件设计和开发过程中,选择一个合适的、一定程度可扩展的软件架构是非常重要的。我们在学校里或者一些“正统”技术书籍接触到的架构,往往偏理论化,例如UML、建模之类的,或者偏解决方案层面的架构,对于纯粹客户端的软件架构讨论得并不太多;介绍设计模式的书很多,但为了具体剖析某一种设计模式,往往把问题进行抽象简化或聚焦,这容易使读者不能方便的联系到实战,这样学习的效果也会打了折扣。

  现在有非常多优秀的开源项目,其实最好的方法就是读这些开源项目的代码去学习其中的软件架构设计方法。本文讨论一些读代码和做设计的体会,希望抛砖引玉,在这方面引发大家更多的讨论。
  优秀的开源项目代码量通常都非常庞大,很多时候看到代码第一反应是有些恐惧的,感觉无从下手。因为代码太复杂了,完全不清楚里面有些什么东西,稍微调试一下,发现流程调来调去,一会就晕了。这种情况下,首先就是不用慌乱和着急,要相信代码都是程序员写出来的,别人能写出来,我们自己要是在那个位置那个处境,也是能写出来的,要看懂就更加没问题了。可以尝试以下的步骤,静下心来读读代码。

第一步:寻找已有知识,帮助了解总体框架

寻找并消化已有知识是帮助我们读懂代码投入最少收获最大的手段。通常来说就是问人或者在网上找现有的资料,这个时候个人过去积累的圈子和经验是比较重要的,能够知道问什么人,如何提出问题,如何找到资料。掌握已有知识的过程需要抓住一些比较核心主干的内容,一些局部的细节可以不完全了解。

第二步:确定自己本次要分析的是哪部分

  代码是很多很复杂的,一个流程就会牵涉到非常多的内容,一次分析不要试图把所有相关的内容都分析清楚,因此可以先确定一次分析的内容的范围,范围之外的东西只要了解基本的概念就行了。比如,分析事件管理,只看事件管理内部运行机制就可以了,至于业务都用到了些什么事件,这些事件的响应是怎么做的,不需要管;比如分析一个软件工作的主流程,这个软件可能用到了消息循环或IPC,在分析主流程的时候我们可以认为IPC和消息循环是一个黑盒,只要知道有这么个东西就行了。

  有时候,当分析某个东西的过程中,会发现可能需要先了解其他的部分,这时候也可以先停下本次主题的分析,先再起一个另外的一个分析主题,先分析另一部分。比如在分析软件主流程的时候,发现其中的消息循环是主流程里关键的设计,那么可以先把主流程的分析停下来,先单独分析消息循环,等把消息循环搞明白了,再接上来继续分析主流程。

第三步:大概的看一下所分析部分相关的代码,有些什么类,大概的类层次
  
  这个阶段,还不需要真正的写文档,通常可以大概看一下有些什么类,猜一猜各个类是什么意思,一些简单的类关系可以先在纸上随便画一画,然后再想几个CASE,简单调试运行一下,大概看一下程序的执行流程。

  总而言之,这一步是大概的过程,我们需要尽量去猜一些总体上的信息,包括我们要分析的部分会负责哪些工作,大概的流程会是怎么样的,复杂点在什么地方。

第四步:深入的思考

  这一步很重要。
  经过第三步之后,我们大概知道有些什么类,需要做些什么事情,但我们还不知道这些类具体的关系,准确的流程是怎么样的。这时候我们最好可以自己来抽象一下,看看能不能尝试回答一些自己提出的问题:在需要做的这些事情里面,哪些是经常变化的?

  (架构设计需要封装变化)
  如果我来做这个设计,有可能怎么做?
  上面的类哪些是关键的类?
  以前有没有见过类似的设计,去解决类似的问题的?

第五步:开始系统的阅读代码,画类图,写文档

  经过第四步的思考,我们通常会有一些感觉,因为在思考时我们会去看看代码来验证一下自己的思考,当然经过第四步后我们还是有很多东西是不清楚的,这时候就做第五步。

  做这一步的时候,基本上会先看一下主要的类的属性和方法,看的时候尽量关注最重要的属性和方法。需要注意的是,有时候一个类负责的事情很多,有些事情不是在我们本次分析范围之内的,我们可以先不管它(比如一个类,其中可能既有业务处理的内容,也有事件接收处理的内容,如果我们当前分析的主题是事件管理,那么只关心他作为事件接收者的身份就可以了,其他的内容可以先不管)。这时候我们需要尝试画类图,类图上标明主要的属性和方法以及主要的类关系。

  注意一点是:画类图过程中的思考是非常重要的。我记得我以前看代码的时候,仿佛画类图就是一个机械活,画类图的时候我们就要抓住类的本质了,这里面当然最好能够熟悉一些基本的设计模式和常规的命名。另外在画类图的过程中,要随时把自己的任何一点心得写下来,把自己的问题也写下来(比如为什么类A要从类B继承,为什么类A里要包含指向B的指针等等),后续我们需要在不断分析代码过程中去解答这些问题。

第六步:深入阅读代码,调整类图,分段写出文档
  上一步写出的类图有可能是很乱的,也可能不完整,在这一步我们需要做调整,而且需要深入的阅读代码,当然这个过程会伴随深入的调试。在这个过程中,我们需要不断的问自己:它为什么要这么设计?我可不可以另外的设计?每个类的职责是否我刚才理解的内容?

  在分析的过程中,我们需要分段的来进行梳理。当把某一段完整的分析明白之后,我们就会舒一口气。在深入的分析过程中,一定要做文字的记录,这个过程也是梳理自己思路的一个很好的方式。在分析的过程中,我们可能遇到一些障碍,想不清楚了,这时候也不用紧张,先休息一下,可以第二天再看,可以离开代码慢慢的想一想,如果我来做一段,我会怎么做。要相信,肯定是能看懂的。

  在分析的过程中,个人的积累是比较重要的,要把自己的思维跳出现有的代码,把问题去匹配已
有的一些概念、设计或技术,也许突然就明白了。比如当看到E v e n t Si n k的时候,可能想到C O M里的ConnectionPoint,如果记得不是很清楚了,就可以去复习一下ConnectionPoint的具体内容,通常会对理解现有的代码有帮助,并且还可以进一步问自己现有代码和已有的技术或架构有什么区别。还需要注意在深入分析的过程中,一定要抓住主要的部分,把影响自己理解的枝节内容大胆的砍掉(比如错误处理、特殊情况的分支处理等)。

  另外需要注意的是,不必完全迷信代码,要有自己的看法,敢于对现有的设计提出质疑。即使非常优秀的项目,在某些局部的设计上也可能是冗余的,甚至是垃圾的。

第七步:整体梳理


  经过第六步,基本都清楚了,再整体梳理一下就好了,可以跑几个CASE验证一下,也可以对其中用到的设计模式做一些总结和梳理,进一步加深自己对于各种设计模式的理解。

  不论是设计模式所提及的,还是各种架构设计所关注的,核心的内容都是“封装(隔离)变化”,这里是两个关键词:封装与变化。通常,技术人员往往非常重视“封装(隔离)”而忽略“变化”,因为封装过程最能够让技术人员显得很牛,也很有成就感;而变化的分析更多是业务层面的事情,动动嘴皮子而已,实在聊无趣味。这是一个比较严重的认识上的误区。

  在我们所见过的软件代码中,存在许多“无设计”的软件代码,代码想到哪写到哪,软件稍微复杂一点之后就难以维护了,这是被广大优秀程序员所鄙视的;然而另外一种情况却经常被我们忽视甚至引以为荣,那就是“过度设计”。一个技术人员为了显示自己的架构设计能力,不考虑项目业务和团队成员特点,恨不得把学习过的设计模式全部用上,一件简单的事情非要绕一大圈再完成,这种东西在一开始容易“看起来很美”,其实是脱离实际,是对产品和团队很大的伤害

  举一个简单的例子:在Chrome中关于消息循环的设计是比较复杂的,包括了多种类型的消息循环(ForUI、ForIO等),支持多种操作系统(Windows、Mac等),这是Chrome这个产品决定需要这样的设计支持,因此在它的消息循环设计里需要考虑把特定操作系统相关的逻辑、特定消息循环类型相关的逻辑、Chrome业务相关的逻辑这些做很好的封装隔离。而如果我们做另外一个根本不需要考虑支持多操作系统的软件,需要消息循环,却一上来就照搬Chrome里的消息循环的设计,这样的设计对于其他团队成员就是很大的负担了。

  另外一个常见的设计问题是“随意设计”,尤其是当一个团队中有多个有一定经验的开发人员时更容易出现。比如软件某个地方要用到事件,技术人员A很快就完成了一个事件管理机制的设计和实现;过了两天,技术人员B也要用到事件,他根本没去想别人是不是做过事件管理(或者知道别人做过但不屑于用),自己也顺手写了一个;然后技术人员C又写了一个,导致一个软件里事件管理就有好几套。而团队里的一些新人很多又是copy/paste型的,要用到事件管理时,随意找到一个,也不去了解背后的本质,放到代码里运行一试,没出错就万事大吉了。时间长了,同样导致代码很难维护。

  还有一点很重要的,就是架构设计人员要充分了解所负责软件的业务特点、发布节奏、短期和长期规划,同时更要非常清楚的了解自己团队内的技术人员的特点,包括其个人的技术储备、学习能力等,在设计的初期跟所有团队成员在产品节奏和设计理念取得一致,在必要的情况下制定一定的规范,这样才能做出适合自己产品和团队的设计,一定要避免闭门造车、自得其乐式的设计。



作者:liumangxiong 发表于2012-3-19 9:38:24 原文链接

页: [1]
查看完整版本: 通过读代码学习软件架构设计