原始链接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)
不要实现没有必要实现的东西。
为什么这么做
- 为了完成以后需要来完成的功能付出努力,意味着你会减低为当前迭代周期付出的努力。
- 可能会导致代码膨胀,软件将会变得越来越复杂。
如何做
- 只实现当前系统中必须实现的东西而不是那些你所能遇见到的将来可能会需要来实现的,那些东西以后再实现。
参考资料
- http://c2.com/xp/YouArentGonnaNeedIt.html
- http://www.xprogramming.com/Practices/PracNotNeed.html
- http://en.wikipedia.org/wiki/You_ain’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)
将一些方法比较多的接口拆分成多个小的接口。接口应该更依赖于调用它的代码,而不是实现它的代码。原则描述
- 避免过于庞大的接口。类不应该实现除了它的职责的方法。
参考文献
- Interface segregation principle
命令查询分离(Command Query Separation)
命令查询分离原则声明,每个方法都应该要么是执行操作的命令,要么是将数据返回给调用者的查询,而不是两者都执行。
有了这个原则,程序员就可以更加自信地编写代码了。查询方法可以在任何地方和任何顺序使用,因为它们不会改变状态。有了命令,你必须更加小心。如何做
- 将每个方法作为一个查询或一个命令来实现。
- 将命名约定应用于方法名,这意味着该方法是一个查询还是一个命令。
参考文献
- Command Query Separation in Wikipedia
- Command Query Separation by Martin Fowler