编程原则

原始链接principle

每一个程序员都应该理解并使用编程原则以及设计模式,并能从中受益。

通用原则

KISS(Keep It Simple Stupid)

大多数系统在保持简单而不是复杂的情况下运行得最好。

为什么这么做

  • 代码越少,编写的时间就越少,bug也就越少,并且更容易修改。
  • Simplicity is the ultimate sophistication.(简化是最终的高雅?)
  • 完美并不是说系统达到了无法添加的程度,而是指达到了没有任何可以继续简化的程度。

    参考资料

  • KISS Principle
  • Keep It Simple Stupid (KISS)

    YAGNI(You aren’t gonna need it)

    不要实现没有必要实现的东西。

为什么这么做

做最简单的事(Do The Simplest Thing That Could Possibly Work)

为什么这么做

  • 为这个问题寻找最简单的解决方案,而不是将他复杂化。这样我们可以回归到问题的本身。

    如何做

  • 问你自己:“解决这个问题最简单的方案是什么?“

    参考资料

  • Do The Simplest Thing That Could Possibly Work

关注点分离(Separation of Concerns)

关注点分离是一种设计原则,用于将计算机程序分离成不同的部分,这样每个部分都处理一个单独的关注点。例如,应用程序的业务逻辑是一个令人关注的问题,而用户界面则是另一个关注点。更改用户界面不需要对业务逻辑进行更改,反之亦然。
Edsger W. Dijkstra

It is what I sometimes have called “the separation of concerns”, which, even if not perfectly possible, is yet the only available technique for effective ordering of one’s thoughts, that I know of. This is what I mean by “focusing one’s attention upon some aspect”: it does not mean ignoring the other aspects, it is just doing justice to the fact that from this aspect’s point of view, the other is irrelevant.

为什么这么做

  • 简化软件应用程序的开发和维护
  • 当关注点分离时,单独的部分可以被重用,也可以独立地开发更新

    如何做

  • 将程序功能分解成多个模块,尽可能少地重叠。但是也要注意别分的太散,不然将带来更大的复杂度。

    参考资料

  • Separation of Concerns

    保持代码干燥,尽量减少重复代码(Keep Things DRY)

    程序中的每一个部分都必须在一个系统中是单一的、明确的、权威的表示。程序中的每一项重要功能都应该只在一个地方实现。在不同的代码片段中执行相似的功能时,将他们重合的部分提取出来。

    为什么这么做

  • 重复(无意的或有目的的重复)会导致代码难以维护、分解以及逻辑上的矛盾。
  • 对系统中的任何单个功能进行修改时,不需要对其他逻辑无关的功能代码进行更改。
  • 此外,逻辑上相关的代码的改变都是可预测以及一致的,因此需要保持同步。

    如何做

  • 将业务规则、长表达式、if语句、数学公式、元数据等放在一个地方。
  • 确定系统中所使用的单一的、确定性的知识源,然后使用该源生成该知识的可应用实例(代码、文档、测试等)
  • 应用 Rule of three原则).

    参考资料

  • Dont Repeat Yourself

    写代码的时候考虑代码的可维护性(Code For The Maintainer)

    为什么这么做

  • 到目前为止,维护是任何项目中最昂贵的阶段。

    如何做

  • 成为一个代码的维护者。
  • 把维护你的代码的人想象成一个知道你住处的精神病患者,如果你的代码写的不好随时都可能过来干掉你。
  • 确定系统中所使用的单一的、确定性的知识源,然后使用该源生成该知识的可应用实例(代码、文档、测试等)
  • 良好的编写代码和注释,如果一个新手拿起代码,他们就会乐于阅读和学习。
  • Don’t make me think.
  • Use the Principle of Least Astonishment

    参考资料

  • http://c2.com/cgi/wiki?CodeForTheMaintainer
  • http://blog.codinghorror.com/the-noble-art-of-maintenance-programming/

避免过早优化(Avoid Premature Optimization)

Donald Knuth:

程序员浪费了大量的时间去思考,或者担心他们的程序的非关键部分的速度。而当考虑到调试和维护时,这些效率的尝试实际上会产生强烈的负面影响。我们应该忘记小的效率,大约97%的时间:过早的优化是所有邪恶的根源。然而,我们不应在那关键的3%中放弃我们的机会。

当然,理解什么不是“过早”是至关重要的

为什么这么做

  • 在开发的早期,影响系统性能的问题并不会浮现的很清楚。
  • 在优化之后,代码可能更难读懂和维护。

    如何做

  • Make It Work Make It Right Make It Fast
  • 只有在你真正需要的时候才去优化代码,并且在你明白系统的瓶颈后才去优化他。

    参考资料

  • http://en.wikipedia.org/wiki/Program_optimization
  • http://c2.com/cgi/wiki?PrematureOptimization

    重构代码时需要考虑的问题——童子军原则(Boy-Scout Rule)

    为什么这么做

  • 当对现有代码库进行更改时,有可能会降低代码的质量。

    如何做

  • 确保每次提交都不会降低代码的质量。
  • 当看到一些代码不像它应该的那样清晰,应该设法修复它。

    参考资料

  • http://martinfowler.com/bliki/OpportunisticRefactoring.html

    模块与模块(类与类)之间的设计原则(Inter-Module/Class)

    最小化代码的耦合程度(Minimise Coupling)

    模块/组件之间的耦合是它们相互依赖的程度;低耦合是更好的。换句话说,耦合就是在修改模块A后模块B”中断“的概率。

    为什么这么做

  • 一个模块的更改通常会导致其他模块的更改产生连锁反应。
  • 模块的组合可能需要更多的努力和/或时间,因为模块之间的依赖性增加了。
  • 由于必须包含依赖模块,一个特定的模块可能更难复用和测试。
  • 开发人员可能害怕更改代码,因为他们不确定可能会带来什么影响。

    如何做

  • 消除、最小化和减少必要的关系的复杂性。
  • 通过隐藏实现的细节,耦合程度就降低了。
  • 应用迪米特法则(迪米特法则)。

    参考资料

  • Coupling)
  • Coupling And Cohesion

(迪米特法则)Law of Demeter

不要和陌生人说话(Don’t talk to strangers)

为什么

  • 这样做会增加了耦合的程度
  • 这样做会暴露太多的实现细节

    如何做

  • 限制一个对象中的方法,让它只能访问:
    • 这个对象本身
    • 这个方法的参数
    • 在这个方法中创建的对象
    • 这个对象的属性和域。

      参考资料

  • 迪米特法则
  • The Law of Demeter Is Not A Dot Counting Exercise

组合优先用于继承(Composition Over Inheritance)

原因

  • 降低类之间的耦合
  • 使用继承,子类可以轻松地做出假设,并打破里氏替换原则(LSP)。

    如何做

  • 对LSP(可替代性)进行测试,以决定什么时候继承。
  • 当两个类之间的关系是”has a”或者”uses a”时使用组合,当时”is a”时才使用继承。

    参考资料

  • Favor Composition Over Inheritance

    正交原则(Orthogonality)

    正交性的基本概念是,在概念上不相关的事物在系统中也应该是不相关的。
    正交性与简单性有关:设计越正交,异常就越少。这使得用编程语言学习、阅读和编写程序变得更加容易。正交特征的意义是独立于上下文的;关键的参数是对称性和一致性。

(鲁棒性原则) Robustness Principle

Be conservative in what you do, be liberal in what you accept from others

原因

  • 为了能够改进服务,您需要确保服务提供者能够做出更改以支持新需求,同时对现有客户造成最小的破坏。

    如何做

  • 向其他机器发送命令或数据的代码(或在同一台机器上的其他程序)应该完全符合规范,但是接收输入的服务应该接受不符合条件的输入,只要其含义是明确的。(在输入不符合规范时,也能够进行处理)。

    控制反转(Inversion of Control)

    控制反转是一种设计原则,他能够在一个框架中,将自定义的功能交由一个容器来控制。控制反转使得可重用代码和解决特定问题的代码可以同时使用,并让他们可以在一个应用中进行协作,降低了系统的耦合。

控制反转好处

  • 控制反转是用来提高系统的模块化,并使得系统更容易拓展
  • 将任务的执行和实现进行了分离。
  • 将一个模块集中在它所实现的任务上。
  • 将模块从假设中解放出来,不依赖其他系统是如何做的,而是依赖于合同。
  • 防止更换模块产生的副作用。

    控制反转实现方法

  • 使用工厂模式( Service Locator pattern)
  • 使用服务定位器模式( Service Locator pattern)
  • 使用依赖注入(Dependency Injection)
  • 使用上下文查找(Dependency Injection)
  • 使用模板方法模式(Template Method pattern)
  • 使用策略模式(Strategy pattern)

    参考资料

  • Inversion of Control in Wikipedia
  • Inversion of Control Containers and the Dependency Injection pattern

    模块/类的设计原则(Module/Class)

    高内聚(Maximise Cohesion)

    Cohesion of a single module/component is the degree to which its responsibilities form a meaningful unit; higher cohesion is better.

高内聚的缺点

  • 增加了理解模块的难度。
  • 系统维护的难度增加了,因为域的逻辑变化会影响多个模块,并且一个模块的更改可能会需要相关模块的更改。
  • 重用模块的难度增加,因为大多数应用程序不需要模块提供的随机操作集。

    实现高内聚

  • 将相关联的功能聚合在一起,比如说一个class中。

    里氏替换原则(Liskov Substitution Principle)

    Objects in a program should be replaceable with instances of their subtypes without altering the correctness of that program.

java中的多态,父类引用指向子类对象。

参考资料

  • Liskov substitution principle
  • Liskov Substitution Principle

    开闭原则(Open/Closed Principle)

    类在设计时应该对继承开放,并且对修改封闭。这样的类可以在不修改源代码的情况下修改其行为。

    好处

  • 通过对当前的代码改变最小,提高了系统的可维护性和稳定性。

    如何做

  • 编写可以扩展的类(而不是可以修改的类)。
  • 只暴露需要更改的移动部分,隐藏其他所有内容。

    参考资料

  • Open Closed Principle
  • The Open Closed Principle

    单一职责原则(Single Responsibility Principle)

    一个类永远不应该有超过一个理由去改变。
    每个类都应该有一个单独的职责,而这个职责应该完全由类来封装,而且只有这一个职责、

    好处

  • 提高可维护性:只要修改一个模块或者类。

    如何做

  • 使用Curly’s Law.

    参考资料

  • Single responsibility principle

    隐藏具体的实现细节(Hide Implementation Details)

    一个软件模块通过提供一个接口来隐藏信息(即实现细节),而不泄漏任何不必要的信息。

    好处

  • 当实现更改时,客户端所使用的是接口,不需要更改。(多态)

    如何做

  • 最小化类和成员的作用域。
  • 不要公开公开成员数据。
  • 避免将私有实现细节放入类的接口中。
  • 减少耦合,以隐藏更多的实现细节。

    参考资料

  • Information hiding

    Curly’s Law

    参考资料

  • Curly’s Law: Do One Thing
  • The Rule of One or Curly’s Law

    封装变化(Encapsulate What Changes)

    一个好的设计可以识别那些最有可能改变的点,并将它们封装在API背后。当预期的更改发生时,修改将保留在本地。

    好处

  • 当发生更改时,最小化所需的修改

    如何做

  • 封装API背后的变化,而提供不变的访问方式。
  • 尽可能的将不同的改变分发到这个模块本身,让其本身负责。

    参考资料

  • Encapsulate the Concept that Varies
  • Encapsulate What Varies
  • Information Hiding

    接口分割原则(Interface Segregation Principle)

    将一些方法比较多的接口拆分成多个小的接口。接口应该更依赖于调用它的代码,而不是实现它的代码。

    原则描述

    • 如果一个类实现了不需要的方法,调用者就需要知道该类的方法实现。例如,如果一个类实现了一个方法,但是只是简单地抛出,那么调用者将需要知道这个方法实际上不应该被调用。HiveStatement实现了Statement接口,然而现在Hive现在并不打算支持其中的一些方法,只是简单的throw Exception了。

      如何做

  • 避免过于庞大的接口。类不应该实现除了它的职责的方法。

    参考文献

  • Interface segregation principle

    命令查询分离(Command Query Separation)

    命令查询分离原则声明,每个方法都应该要么是执行操作的命令,要么是将数据返回给调用者的查询,而不是两者都执行。
    有了这个原则,程序员就可以更加自信地编写代码了。查询方法可以在任何地方和任何顺序使用,因为它们不会改变状态。有了命令,你必须更加小心。

    如何做

  • 将每个方法作为一个查询或一个命令来实现。
  • 将命名约定应用于方法名,这意味着该方法是一个查询还是一个命令。

    参考文献

  • Command Query Separation in Wikipedia
  • Command Query Separation by Martin Fowler