设计原则

前言

设计原则只是供开发人员参考的,并不是一定要遵守的规则,但需要了解并尽可能的去满足设计原则。

KISS

Keep It Simple Stupid(保持简单直白)

为什么

  • 更少的代码节省更多的时间,减少更多的Bug,以及更容易修改
  • 简单是复杂的更高水平
  • 完美并不是没有东西可以被添加进来,而是没有东西可以被带走

怎么做

  • 不要使用大部分人难以理解的代码,让其晦涩难懂
  • 不要过度优化,不是封装的越多越深越好,也不是底层函数或者高级语法用的越多越好

YAGNI

you aren’t gonna need it(你不会需要它-不要实现非必要的东西)

为什么

  • 你的时间非常宝贵,任何为将来可能需要的功能做的工作,不但会导致你从当前必须实现的功能分心,而且还浪费了阅读者的时间
  • 这将导致代码臃肿、软件变得庞大且复杂

怎么做

  • 在日常开发中,需要尽可能评估好设计的复杂度,不要过度设计。发现自己设计有不足的地方,,要有持续重构的精神,不断完善自己的设计,不要等到事情累计到不得不重构的时候,那时可能影响范围已经很大,事情往往已经到了积重难返的阶段

KISS和YAGNI的异同

KISS和YAGNI都是在阐述简单这个事情,只是看待的角度不同,KISS是从如何做(保持简单)的角度,YAGNI是从要不要做(不要过度设计)的角度

DTSTTCPW

Do The Simplest Thing That Could Possibly Work(做最简单的事情,如果它可能工作)

为什么

  • 如果我们致力于解决真正的问题,就能最大限度地解决真正的问题
  • 专业程序员就是能写出白痴程序员都能读懂的代码,白痴程序员就是写出即使专业程序员都无法读懂的代码

怎么做

  • 询问自己:完成这件事最简单的方式是什么

Separation of Concerns

关注点分离是将计算机程序分离成不同部分的设计原则,这样每个部分都单独处理自己的关注点。例如,业务逻辑与非业务逻辑是不同的关注点,更改业务逻辑不应该影响到非业务逻辑

为什么

  • 简化软件的开发和维护
  • 当每个关注点分离足够好时,代码的可复用性更高且开发及迭代更加具备独立性

怎么做

  • 将程序尽可能分离成各自独立的单一模块中

DRY

Dont’t Repeat Yourself(不要写重复的代码)

系统中的每一个部分都应该单一、清晰、且具备权威性。项目中每一个重要的功能应该仅在一份源代码中,如果相似的功能出现在两个不同的地方,通过抽象出不同部分将其结合起来往往是有益的

为什么

  • 重复可能导致维护困难、因果颠倒、逻辑互斥
  • 修改系统中任何一个单一的部分,不会改变到其他没有关联的地方
  • 逻辑上相关联的部分修改时,可以保持可预测及同步性

怎么做

  • 将业务规则、长的表达式、if语句、数学公式、元数据等等,放在一个地方
  • 在代码中使用单一、明确的来源
  • 当相似功能的代码出现在不同位置3次时,就应该重构这段代码(为什么不是两次,因为过早的重构可能会导致错误的抽象,而3次时,维护的成本已经超过了重构的成本,以及可能潜在的糟糕设计)

PS

我最怕那些copy别人一大段代码的!!!简直噩梦!!!关键我还碰到了!!!苍天呐!!!

Code For The Maintainer

为维护者而编写

为什么

  • 目前为止,维护是任何一个项目最昂贵的阶段

怎么做

  • 做一个维护者
  • 编写代码时,要将维护你代码的人,当成知道你居住地址且有暴力倾向的精神病患者
  • 编写代码和注释时,要关照级别较低的人,让他们也能够愉快的阅读并能从中学习
  • 不要让他人思考
  • 遵循最小惊讶原则(设计应该按照大多人期望的形式运行,而不是让其感到惊讶或者诧异)

Avoid Premature Optimization

避免过早优化(理解过早和非过早至关重要)

为什么

  • 目前还不清楚瓶颈在哪里
  • 优化后,可能变得更加难以阅读及维护

怎么做

  • 让它更加正确、更加快速
  • 不要在不需要时优化,而是在分析及发现瓶颈后再优化

Minimise Coupling

最小化耦合(耦合是模块/组件间项目依赖的程度)

为什么

  • 一个模块的改变经常会对其他模块造成影响

  • 由于模块间的相互依赖,使用及组装可能花费更多的时间

  • 模块间相互依赖程度高容易导致复用性及可测试降低

怎么做

  • 消除或者减少不必要的联系
  • 通过隐藏实现细节来减少耦合
  • 遵循迪米特法则

Law of Demeter

迪米特法则(不要和陌生人说话。如果两个类无需直接通信,那么就不应该发生直接的相互调用,而应该通过第三方转发调用)

为什么

  • 直接通常导致增强耦合
  • 直接通常显示了过多的实现细节

怎么做

一个对象的方法只能通过一下方式调用:

  • 对象本身
  • 方法中的一个参数
  • 方法中被创建的对象
  • 对象中的直接属性或字段

Composition Over Inheritance

组合优于继承

为什么

  • 类之间的耦合更低
  • 继承打破封装

怎么做

  • 继承前考虑父类是否有缺陷,而你是否愿意将缺陷传播到你的类中
  • [has a]或者[use a]的类关系用组合;[is a]的情况,才能使用继承

Orthogonality

正交性,系统中概念上不相干的事情不应该联系在一起

为什么

  • 设计越正交,异常越少,使得学习、阅读、编写代码更加容易

Robustness Principle

伯斯塔尔定律,对你想要做的要求严格,对你从其他人那里接收的保持宽松

为什么

为了在服务迭代过程中,提供者能够做出新的需求,同时对现有客户造成最小的影响

怎么做

发送给其他机器的代码或者是命令,应该确保完全符合规范。但是接收的输入应该接受不符合规范的,只要含义清楚。

Inversion of Control

控制反转原则,也叫好莱坞原则(不要打电话给我,我们会打电话给你),使可重用的代码和解决具体问题的代码,即使他们在一个应用里

为什么

  • 控制反转被用来增加程序的模块化以及可扩展性
  • 将任务的执行与实现解耦
  • 将模块专注于他们所涉及的任务
  • 防止模块被替换带来的副作用

怎么做

  • 使用工厂模式
  • 使用服务定位模式
  • 使用依赖注入
  • 使用上下文查找
  • 使用模板方法模式
  • 使用策略模式

Maximise Cohesion?

最大化内聚,

Liskov Substitution Principle

里氏替换原则,程序中的对象可以被其子类替换,而不改变程序的正确性

Open/Closed Principle

开闭原则,软件实体应该对扩展开放,对修改关闭

为什么

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

怎么做

  • 编写可以扩展的类(而不是可以修改的类)

  • 仅暴露可移动可修改的部分,隐藏其他部分

Single Responsibility Principle

单一责任原则,一个类不应该有超过一个需要改变的理由

为什么

  • 可维护性:改变应该仅在一个模块或者类中进行

怎么做

  • 参考Curly’s Law

Hide Implementation Details

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

为什么

  • 当一个实现改变,使用接口的客户端不用进行更改

怎么做

  • 最小化类和变量的可访问性
  • 不要将变量数据设置为public
  • 避免放置私有的实现细节放到类的接口上
  • 减少耦合以隐藏更多的实现细节

Curly’s Law

科里定律,为任何特定代码选择一件单一、明确的目标,做一件事情

Encapsulate What Changes

封装经常修改的代码

为什么

  • 当改变发生时,减少需要修改的地方

怎么做

  • 封装API背后不同的概念
  • 尽可能将不同概念分离到它自己的模块

Interface Segregation Principle

接口隔离原则。将臃肿的接口减少为多个更小、更具体的客户端特定接口。接口应该更依赖于调用它的代码,而不是实现它的代码。

Command Query Separation

命令查询分离,每个方法要么是执行操作的命令,要么是向调用者返回数据的查询,但不能两个同时存在。这样就能使得使用者更加方向的调用。

为什么

  • 通过清除的将发放划分为查询和命令,程序员可以在不知道方法实现细节的情况下,更加有信心进行编码

怎么做

  • 将一个方法作为查询或命令实现
  • 明确方法的名称,通过约定名称的方式告知是查询还是命令

Murphy’s Law

墨菲定律,任何可能出错的都会出错

Brooks’s Law

布鲁克斯法则,向已经延误的项目添加人力,只会使其更晚

Linus’s Law

林纳斯定律,给予足够的眼睛关注,所有的bug都会显现,代码检视很重要