设计的臭味

来源:互联网 发布:淘宝特大号女鞋 编辑:程序博客网 时间:2024/04/20 10:26

设计的臭味——腐化软件的气味

 

当软件出现下面任何一种气味时,就表明软件正在腐化。
  • 僵化性(Rigidity):很难对系统进行改动,因为每个改动都会迫使许多对系统其他部分的其他改动。
  • 脆弱性(Fragility):对系统的改动会导致系统中和改动的地方在概念上无关的许多地方出现问题。
  • 牢固性(Immobility):很难解开系统的纠结,使之成为一些可在其他系统中重用的组件。
  • 粘滞性(Viscosity):做正确的事情比做错误的事情要困难。
  • 不必要的复杂性(Needless Complexity):设计中包含有不具任何直接好处的基础结构。
  • 不必要的重复(Needless Repetition):设计中包含有重复的结构,而该重复的结构本可以使用单一的抽象进行统一。
  • 晦涩性(Opacity):很难阅读、理解。没有很好的表现出意图。

1 僵化性

    僵化性是指难以对软件进行改动,即使是简单的改动。如果单一的改动会导致有依赖关系的模块中的连锁改动,那么设计就是僵化的。必须要改动的模块越多,设计就越僵化。
    大部分的开发人员都以这样或者那样的方式遇到过这种情况。他们会被要求进行一个看起来简单的改动。他们看了看这个改动并对所需的工作做出了一个合理的估算。但是过了一会儿,当阿门实际进行改动时,会发现有许多改动带来的影响自己并没有预测到。他们发现自己要在庞大的代码中搜寻这个变动,并且要更改的模块数目也远远超出最初的估算。最后,改动所花费的时间要远比初始估算长。当问他们为何估算的如此不准确时,他们会重复软件开发人员惯用的悲叹,“它比我想像的要负责的多!”
 

2 脆弱性

    脆弱性是指,在进行一个改动时,程序的许多地方就可能出现问题。常常是,出现新问题的地方与改动的地方并没有概念上的关联。要修正这些问题就又会引出更多的问题,从而使开发团队就像一只不停追逐自己尾巴的狗一样(忙的团团转)。
    随着模块脆弱性的增加,改动会引出意想不到的问题的可能性就越来越大。这看起来很荒谬,但是这样的模块使非常常见的。这些模块需要不断的修补——它们从来不会被从错误列表中去掉,开发人员知道需要对它们进行重新设计(但是谁都不愿意去面对重新设计中的难以琢磨性),你越是修正它们,它们就变得越糟。
 

3 牢靠性

    牢靠性使指,设计中包含了对其他系统有用的部分,但是要把这些部分从系统中分离出来所需要的努力和风险使巨大的。这是一件令人遗憾的事,但却是非常常见的事情。
 

4 粘滞性

    粘滞性有两种表形形式:软件的粘滞性和环境的粘滞性。
    当面临一个改动时,开发人员常常发现会有很多改动的方法。其中,一些方法会保持设计;而另外一些会破坏设计(也就是生硬的手法)。当那些可以保持系统设计的方法比那些生硬手法更难应用时,就表明设计具有高的粘滞性。做错误的事情时容易的,但是做正确的事情却很难。我们希望在软件设计中,可以容易地进行那些保持设计的变动。
    当开发环境迟钝、低效时,就会产生环境的粘滞性。例如,如果编译所花费的时间很长,那么开发人员就会被引诱去做不会导致大规模重编译的改动,即使那些改动不再保持设计。如果源代码控制系统需要几个小时去check in仅仅几个文件,那么开发人员就会被引诱做那些需要尽可能少check in的改动,而不管改动是否会保持设计。
    无论项目具有哪种粘滞性,都很难保持项目中的软件设计。我们希望创建易于保持设计的系统和项目环境。
 

5 不必要的复杂性

    如果设计中包含有当前没有用的组成部分,它就含有不必要的复杂性。当开发人员预测需求的变化,并在软件中放置了处理那些潜在的变化的代码时,常常会出现这种情况。起初,这样做看起来像是一件好事。毕竟,为将来的变化做准备会保持代码的灵活性,并且可以避免以后再进行痛苦的改动。
    糟糕的是,结果常常正好相反。为过多的可能性做准备,致使设计中含有绝不会用到的结构,从而变得混乱。一些准备也会带来回报,但是更多的不会。期间,设计背负着这些不会用到的部分,使软件变得复杂,并且难以理解。
 

6 不必要的重复

    剪贴(cut)和粘贴(copy)也许使有用的文本编辑(text-editing)操作,但是它们却是灾难性的代码编辑(code-editing)操作。时常,软件系统都是构建于众多的重复代码片段之上。例如:
    Ralph需要编写一些完成某项功能的代码。他浏览了一下他认为可能会完成类似工作的其他代码,并找到了一块合适的代码。他将那块代码copy到自己的模块中,并做了适当的修改。
    Ralph并不知道,他用鼠标获取的代码是由Todd放置在那里的,而Todd是从Lilly编写的模块中获取的。Lilly是第一个完成这项功能的,但是她认识到完成这项功能和完成另一项功能非常类似。她从别处找到了一些完成另一项功能的代码,cut、copy到她的模块中,并做了必要的修改。
    当同样的代码以稍微不同的形式一再出现时,就表示开发人员忽视了抽象。对于他们来说,发现多有的重复并通过适当的抽象去消除它们的做法可能没有高的优先级别,但是这样做非常有助于使系统更加易于理解和维护。
    当系统中有重复的代码时,对系统进行改动会变得困难。在一个重复的代码体中发现错误必须要在每个重复体中一一修正。不过,由于没有重复体直接都有细微的差别,所以修正的方式也不总是相同的。
 

7 晦涩性

    晦涩性是指模块难以理解。代码可以用清晰、富有表现力的方式编写,或者可以用晦涩、费解的方式编写。代码随着时间而演化,往往会变得越来越晦涩。为了使代码的晦涩性保持最低,就需要持续的的保持代码清晰并富有表现力。
 

解决之道

用面向对象的一下原则来指导实践:
  • 单一职责原则(The Single Responsibility Principle,简称SRP)
  • 开放——封闭原则(The Open-Close Principle,简称OCP)
  • Liskov替换原则(The Liskov Substitution Principle,简称LSP)
  • 依赖倒置原则(The Dependency Inversion Principle,简称DIP)
  • 接口隔离原则(The Interface Segregation Principle,简称ISP)