软件设计原则

200 阅读10分钟

Generic[通用原则]

KISS原则 [Keep Is Simple Stupid]

大多数系统简单设计以后,就能运行的很好。并不一定要设计复杂。

原因:

  • 少量的代码更少的时间去写,有更少的bug,更加容易去修改
  • 简单是最复杂的事情
  • 完美的代码不是没有可以代码可以添加的时候,而是不能进行删除代码的时候。

YAGNI[you aren't gonna need it]

除非必要,不要去实现某些东西

原因:

  • 任何工作只用于明天可能需要的某一些特性,都意味着失去了当前迭代完成工作的一些特性。

  • 这会导致代码膨胀,软件设计变得更加复杂

解决:

  • 当你真正需要的时候才去实现它,而不是在你只是以为未来会使用的时候。

Do The Simplest Thing That Could Possibly Work

原因:

  • 如果我们考虑真正的问题所在,解决真正问题的进展就会最大化 解决:
  • 问问自己“最简单可行的方法是什么?”

Separation of Concerns[关注分离点]

关注分离点是将计算机程序分成不同部分的设计原则,这样每个部分就可以处理单独的关注点。例如:应用程序的业务逻辑是一个关注点,用户界面是一个关注点。更改用户界面不应该需要更改业务逻辑。反之亦然。

原因:

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

解决:

  • 将程序功能分解为尽可能少重叠的独立模块

Keep Things DRY

在系统中,每条知识都必须有一个单一的、明确的、权威的表示。

程序中的每个重要功能都应该在源代码的一个地方实现。如果类似的函数是有不同的代码片段执行组成的,那么就通过抽象出不同的部分,然后将他们组合起来会更好。

原因:

  • 重复(无意的或有目的的重复)可能导致维护噩梦、糟糕的理据和逻辑矛盾

  • 对系统中任何单个元素的修改并不要求对其他逻辑上不相关的元素进行修改

  • 逻辑上相关的元素都是可预测的、一致的变化,因此保持同步

解决:

  • 将业务规则,长表达式,if语句,数学公式,元数据等只放在一个地方

  • 识别系统中使用的每个知识片段,确保单一性,确定的来源。最好系统中都有该使用用例。

Code For The Maintainer

原因:

  • 维护在任何项目中都是最昂贵的

解决:

  • 成为这个项目的维护人员

  • 编写代码的生活,一定要把下一个维护你代码的人当成一个知道你住在哪里的暴力精神病患者【防止被揍】

  • 让别人一看就会,不需要经过思考逻辑

  • 经常按照这样一种方式来写代码和写注释,如果一个初级开发工程师看到了这些代码,他们会从阅读代码和学习中获得乐趣

Avoid Premature Optimization[避免过早的优化]

引用Donald Kunth:

Programmers waste enormous amounts of time thinking about, or worrying about, the speed of noncritical parts
of their programs, and these attempts at efficiency actually have a strong negative impact when debugging 
and maintenance are considered. We should forget about small efficiencies, say about 97% of the time: 
premature optimization is the root of all evil. Yet we should not pass up our opportunities in that critical 3%.

理解什么是过早的,什么不是过早的至关重要的。

原因:

  • 瓶颈出现在哪里是未知的

  • 优化之后,可能更难阅读和维护

解决:

  • 维护以后 确实有用,正确,快速

  • 除非需要,否则不进行优化,只有在分析以后发现瓶颈才进行优化

Boy-Scout Rule[童子军规则]

美国童子军有一个简单的规则,我们可以应用于我们的职业:“让露营地比你发现的更干净”。童子军规则规定,我们应该总是让代码比我们发现它时更干净。

原因:

  • 当对现有代码库进行更改时,代码质量往往会下降,从而积累技术债务。按照童子军的规则,我们应该注意每一件事的质量。持续的重构抵制了技术债务,无论多么小

解决:

  • 每次提交代码的时候都要确保不会降低代码的质量

  • 任何时候有人看到一些不太清晰的代码,都应该抓住机会立即修复它

Inter-Module/Class

Minimise Coupling[最小耦合]

模块/组件之前的耦合越低越好。两个模块之前的关联越低越好。

原因:

  • 一个模块中的代码更改通常会影响其他模块中的代码

  • 由于模块之前依赖关系的增加,模块组装可能需要更多的工作和时间

  • 特定的模块可能会更难使用

  • 开发人员可能会害怕更改代码,因为不确定影响什么

解决:

  • 功能最小化,减少必要关系的复杂性

  • 通过隐藏实现细节,可以减少耦合

Law of Demeter[迪米特法则]

不要和陌生人说话

原因:

  • 增加耦合性

  • 透漏太多的实现细节

解决:

  • 对象的方法只能调用:

  • 对象本身

  • 方法的参数

  • 在方法中创建的任何对象

  • 对象的任何直接属性/字段

Composition Over Inheritance[继承 组合]

原因:

  • 类之间的耦合更少

  • 使用继承,子类很容易做出假设,并打破LSP

解决:

  • 测试LSP,决定何时继承

  • Orthogonality

  • 不相干的事务,不应该在系统中有关联

Robustness Principle[健壮性]

在你做的事情上要保守,如果你从别人哪里接受信息要自由

协作服务依赖于彼此的接口。接口经常会发生变化,导致另一端接受未指定的数据。如果接收到的数据没有严格遵守规范,幼稚的实现将拒绝协作。更复杂的实现仍然可以忽略它不能识别的数据。

原因:

  • 为了服务的演进,需要确保提供者能够进行更改以支持新的需求,同时对客户端造成最小的伤害

解决:

  • 向其他机器(或同一机器上的其他程序)发送命令或数据的代码应该完全符合规范,但接收输入的代码应该接受不符合规范的输入,只要意义明确

Inversion of Control[控制反转]

控制反转也被称为好莱坞法则,“不要给我们打电话,我们会给你打电话”。计算机程序的自定义编写部分从通用框架接受控制流。 控制反转的含义是:可重用代码和解决特定问题的代码要独立开发,即使他们是一个程序中的运行。

原因:

  • 增加程序的模块化,使其可扩展

  • 将任务的执行与实现解耦

  • 将模块从关于其他系统如何执行它们的任务的假设中解放出来,取而代之的是依赖契约

  • 避免更换模块时产生的副作用

解决:

  • 使用工厂模式

  • 使用服务定位器模式

  • 使用依赖注入 [DI]

  • 使用更符合实际的查找

  • 使用模板方法模式[Template Method Pattern]

  • 使用策略模式[Strategy Pattern]

Module/Class

Maximise Cohesion[高内聚]

单个模块/组件的内聚性是其职责形成有意义的单元的程度;凝聚力越高越好。

原因:

  • 增加了理解模块的难度

  • 增加了维护系统的难度,因为域中的逻辑更改会影响多个模块,而且一个模块中的更改需要相关模块中的更改

  • 增加了重用模块的难度,因为大多数应用程序不需要模块提供的随机操作集

解决:

  • 将共享单一职责的相关功能分组(例如在一个类中)

Liskov Substitution Principle [里式替换原则]

程序中的对象应该可以用其子类型的实例替换,而不会改变程序的正确性。

Open/Closed Principle[开闭原则]

软件实体(例如类)应该对扩展开放,但对修改关闭。也就是说,这样的实体可以允许在不改变其源代码的情况下修改其行为

原因:

  • 通过最小化对现有代码的更改来提高可维护性和稳定性

解决:

  • 编写可以扩展的类

  • 只暴露需要更改的公共部分,隐藏其他部分

Single Responsibility Principle[单一职责原则]

每个类都应该有一个单独的职责,并且这个职责应该被类完全封装。责任可以定义为更改的原因,因此类或模块应该有且只有一个更改的原因

原因:

  • 可维护性:只需要在一个模块或类中进行更改

解决:

  • 应用大定律

Hide Implementation Details[隐藏实现细节]

软件模块通过提供接口隐藏实现细节,不泄露任何不必要的信息

原因:

  • 当实现更改时,客户端所使用的接口不必更改

解决:

  • 最小化类和成员的可访问性

  • 不要公开成员数据

  • 避免将私有实现细节放入类的接口中

  • 减少耦合以隐藏更多的实现细节

Curly's Law

Curly的法则是关于为任何特定的代码选择一个明确定义的目标:做一件事。

Encapsulate What Changes

好的设计能够识别最有可能更改的热点,并将其封装在API后面。当预期的更改发生时,修改被保留在本地

原因:

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

解决:

  • 封装API背后变化的概念

  • 可能将变化的概念分离到它自己的模块中

Interface Segregation Principle[接口隔离原则]

将臃肿的代码分接为多个更小,更特定的多个接口。接口应该更依赖于调用它的代码,而不是实现它的代码

原因:

  • 如果一个类实现了不需要的方法,那么调用者需要知道该类的方法实现。例如,如果一个类实现了一个方法,但只是抛出,那么调用者将需要知道这个方法实际上不应该被调用

解决:

  • 避免臃肿的接口。永远不能违背单一职责原则。

Command Query Separation

命令查询分离原则规定,每个方法要么是执行操作的命令,要么是向调用者返回数据的查询,但不能同时是两者

原因:

  • 通过将方法清晰地划分为查询和命令,程序员可以在不了解每个方法的实现细节的情况下更有信心地编写代码

解决:

  • 以查询或命令的形式实现每个方法

  • 对方法名称应用命名约定,以暗示该方法是查询还是命令

转载自:

java-design-patterns.com/principles/…