如何更好的组织代码

一、组织代码的原因或意义

  • 代码的编写应当首先让其他人能够看懂,其次才是让机器能够执行。合理组织代码的目的并不是让计算机理解你的代码,而是让其他人能够很好地读懂你所编写的代码,进而在某种程度上高效而自信地维护代码并做二次开发。
  • 当一段代码写得太长并且包含太多元素时,这段代码就会变得非常复杂,不容易让人定位信息、纵览概况,也就很难让人理解清楚代码各部分的功能。解决这个问题最好的方法就是“分而治之”——将复杂的大段代码分解为多个小部分,每个小部分可以分别独立地进行理解。
  • 代码分解或抽象最好的入门方法——SOLID原则(参考:http://www.cnblogs.com/shanyou/archive/2009/09/21/1570716.html):
    • 单一责任原则: 当需要修改某个类的时候原因有且只有一个。换句话说就是让一个类只做一种类型责任,当这个类需要承当其他类型的责任的时候,就需要分解这个类。
    • 开放封闭原则:软件实体应该是可扩展,而不可修改的。也就是说,对扩展是开放的,而对修改是封闭的。这个原则是诸多面向对象编程原则中最抽象、最难理解的一个。
    • 里氏替换原则:一个子类的实例应该能够替换任何其超类的实例,它们之间具有is-A关系。
    • 依赖倒置原则
      • 高层模块不应该依赖于低层模块,二者都应该依赖于抽象。
      • 抽象不应该依赖于细节,细节应该依赖于抽象。
    • 接口分离原则:不能强迫用户去依赖那些他们不使用的接口。换句话说,使用多个专门的接口比使用单一的总接口总要好。

二、组织代码的四大策略

  组织代码的策略大致有四种,分别是:元件组织法工具箱组织法层组织法类别组织法。前三种策略适用于类、包、工程等层次的代码组织,而最后的类别组织法则或多或少地专门针对包层次地代码组织。(参考 http://www.infoq.com/cn/articles/four-strategies-for-organizing-code,这个文章在配图上有点错误。)

  1. 元件组织法
    • 元件组织法可以使代码的复杂程度最小化,它主要关心代码单元(比如包)外部的衔接性和内部的内聚力。外部衔接性是指包拥有最少的接口,接口的功能与元件提供的服务关联性很强;内聚力则是指包内部的代码拥有较强的内在关联性。(类似下图中地完全独立地电子元件)
    • 分解并创建新的代码单元。
      • 创建一个新的代码单元,通常的做法是识别一个或多个已有包中的一部分功能并生成一个新的抽象。这就意味着代码单元的总数变多了,相应地每个代码单元的体量变小了,代码更容易被理解消化。然而这还只是第一步,总体的复杂度还没有降低。


      • 接下来我们需要消除依赖关系。含有相互依赖关系的包不能被视为独立的代码单元,这是因为单独只看一个包的内容并不能完全理解它的代码。在上面的例子中可以直观地看到,Graph类与GraphStorage类关联,GraphStorage类不允许被修改。不仅graph_storage包依赖着许多graph包的域模型,而且这些包相互间也有着依赖关系。最容易消除的依赖关系通常是新创建的包对旧包的依赖。之所以认为这是一个提升,最重要的原因是当我们在阅读storage的代码时,我们可以确定代码功能所涉及的对象都包含在了Storable接口中。


      • 下一步则是消除graph包对storage包的直接关联。举例来说,一种消除关联的方法是在graph包中创建一个GraphPersister接口,让更高一层的包与Graph包对接。这样最大的好处又是使graph包所依赖的storage包的功能变得清晰明确了。

  2. 工具箱组织法
    • 工具箱组织法主要关注外部衔接性,它提供了一种稳定的工具箱,使用者可以从工具箱中选取自己需要的东西。这个策略使用的前提是代码具有很强的内聚力。工具箱一般由接口的互补执行机制组成,使用者可以选取需要的执行机制或是将多个执行机制组合起来使用,但在一次执行时并不同时使用多个机制。
      • 集合库的组织方法就是典型的工具箱组织法,涉及一系列集合接口的互补执行机制,这些集合接口的特性受到时间复杂度、内存占用率等因素的影响。工具箱也可能拥有一个统一的主题,比如只包含基于磁盘的数据结构。
      • 日志库本身不一定是工具箱,但它通常包含一个日志写入器的工具箱。

    • GUI库中的每个元件可能拥有各自的包,但如果给每个元件都建一个工程就会造成不必要的浪费。相似地,每个集合实现可能都分别适用于一个单独的包,然而把它们分别放在不同的包中则会产生大量冗余。不过在这个例子中,为了符合外部一致性原则而保持简洁的外观,一个包含了若干类的集合实现则需要拥有其单独的包。(下图为符合外部一致性原则的DiskList工具箱)


  3. 层组织法
    • 根据部署方案等规则划分层的边界,进而将代码进行分割。Java中比较通用的三层架构就用的时层组织法:View > Service > DAO。


    • 分层的目的是为了“高内聚低耦合”。层组织法可以降低耦合度,但它的重点主要是促进工作流的内聚力,而不是通过最小化跨单元的耦合项来降低代码的复杂程度。这个策略与工具箱组织法不同,层与层之间并不存在一个最小的连贯接口。层接口的构成要素很多,它们可以被用户层中对应的要素分别访问。
    • 实际中应当谨慎使用层组织法,因为层组织策略常常提高而非降低了系统总体的复杂度。不过在某些情况下,层组织法所带来的好处远远大于它的缺点。

  4. 类别组织法
    • 类别组织法适合整理过于复杂的代码单元,它将不同的代码部分放在相应的基于类或接口类别的bucket储存单元中。在这一分类过程中,依赖关系、概念联系以及一些典型的生成包(名称通常为exception、interface、manager、helper、entity等)都被忽略了。
    • 缺点:我认为类别组织法不适合用来组织代码,因为它隐藏了复杂代码实际存在的问题,这样会误导开发者认为代码中的问题已得到修复,然而实质上问题并没有彻底解决,整体的复杂程度也没有降低。类别组织策略另一个比较大的缺点是,在极端情况下要求每一个类都可以划分入一个确切的类别。我曾遇到过这种极端的情况,就为了使所有代码都有一个匹配的包,整个代码库中创建了一些奇奇怪怪的东西,比如代码管理器、帮助器等。
    • 优点:类别组织法在分割大型包方面提供了简单的解决方案,而对于大多数人而言,包的大小并不是主要矛盾,类别组织法成功解决了代码各部分独立性的问题,因此它经常应用于商业性质的软件开发中。

 

posted on 2017-10-19 15:43  笑看风云变幻  阅读(426)  评论(0编辑  收藏  举报