代码设计原则

简介: 代码设计原则

什么是设计模式


设计模式是针对软件开发过程中遇到的一些设计问题,总结出来的一套解决方案或者设计思路。


https://zh.wikipedia.org/wiki/%E8%AE%BE%E8%AE%A1%E6%A8%A1%E5%BC%8F_(%E8%AE%A1%E7%AE%97%E6%9C%BA)

image.png


创建型模式

  • 简单工厂模式(Simple Factory)
  • 工厂方法模式(Factory Method)
  • 抽象工厂模式(Abstract Factory)
  • 创建者模式(Builder)
  • 原型模式(Prototype)
  • 单例模式(Singleton)


结构型模式

  • 外观模式(Facade)
  • 适配器模式(Adapter)
  • 代理模式(Proxy)
  • 组合模式(Composite)
  • 享元模式(Flyweight)
  • 装饰模式(Decorator)
  • 桥模式(Bridge)


行为型模式

  • 中介者模式(Mediator)
  • 观察者模式(Observer)
  • 命令模式(Command)
  • 迭代器模式(Iterator)
  • 模板方法模式(Template Method)
  • 策略模式(Strategy)
  • 状态模式(State)
  • 备忘录模式(Memento)
  • 解释器模式(Interpreter)
  • 职责链模式(Chain of Responsibility)
  • 访问者模式(Visitor)


面向对象


面向对象编程是一种编程方式或者编程风格, 以类或者对象作为组织代码的基本单元。包含封装,多态,继承,抽象等4个基本特性。


面向对象语言

面向对象语言是支持类和对象的语法机制,并有现在的语法机制,能够方便的实现面向对象的4大特征(封装、抽象、继承、多态)的编程语言。封装特性存在的意义,一方面是保护数据不被随意修改,提高代码的可维护性;另一方面是仅暴露有限的必要接口,提高类的易用性。


封装

封装也叫做数据隐藏和数据访问保护,通过暴露有限的访问接口,授权外部仅能通过类提供的方式来访问内部信息和数据。它需要编程语言提供权限访问控制语法来支持, 比如 Java 中的 private 、protected 、public等关键字。


抽象

封装说的是如何隐藏信息和保护数据,抽象说的是如何隐藏方法的具体实现,抽象可以通过接口或者抽象类来实现,但也并不需要特定的语法来支持。抽象存在的意义,一方面是提高代码的可扩展性和维护性, 修改方法时不需要改变定义,减少代码的改动范围,另外一方面也是处理复杂问题的有效手段, 能够有效过滤掉不需要关注的信息


继承

继承用来表示 类之间 is-a 的关系,分为两种模式, 单继承和多继承,单继承表示一个子类只能继承一个父类,多继承表示一个子类可以继承多个父类,为了实现继承这个特性,编程语言需要提供特殊的语法机制来支持。继承主要是用来解决代码复用的问题。


多态

子类可以替代父类,实际代码运行过程中,调用子类的方法实现。多态这个特性需要编程语言的特殊语法机制来实现,比如继承、接口类,duck-typing, 多态可以提高代码的扩展性和复用性,有很多设计模式,设计原则,编程技巧的代码实现基础。


面向对象和面向过程


  • 对于大规模复杂程序的开发,程序的处理并非单一的一条主线,是错综复杂的网状结构,面向对象比面向过程编程,更能够应对这种复杂类型的程序开发。
  • 面向对象编程相对于面向过程编程,具有丰富的特性(抽象,继承,封装,多态),利用这些特性写出的代码更加容易扩展,易复用,易维护。
  • 面向对象编程比面向过程编程更加人性化,更加智能。


组合与继承

  • 继承是面向对象的四大特性之一,表示类之间的 is-a 的关系(Apple is Fruit ) 可以解决代码复用的问题,但是继承如果层次过深,过复杂,也会影响代码的可维护性。
  • 组合表示的是类的 has-a 的关系,比如 House has BathRoom 。

类的继承层次如果很深,继承的关系会越来越复杂,而且层次很深很复杂的继承关系,会导致代码可读性变差,子类的实现依赖父类的实现,两者高度耦和,一旦父类代码修改,会影响子类的逻辑。


GO 是面向对象语言么?


Go 围绕 struct ,提供了私有属性、method、interface、struct 嵌套能力,可以用更轻量的方式实现面向对象(封装,继承,多态,抽象)

Go 核心理论

基于接口编程而非实现编程

接口是一组协议或者约定,是功能提供者提供给使用者的一个功能列表,接口在不同的场景下有不同的解读,

好的代码设计,不仅能够应对当下需求,而且在将来需求发生变化的时候,仍然可以在不破坏原有代码设计的情况下灵活应对。

设计原则


image.png

如何理解单一职责原则 (SRP)


一个类或者一个模块只负责完成一个职责或者功能,不要设计大而全的类, 要设计粒度小,功能单一的类, 单一职责是为了实现代码高内聚,低耦合,提高代码的复用性,可读性,可维护性。

如何判断类的职责是否足够单一

不同应用的场景,不同阶段的需求背景,不同的业务层面,对通一个类的职责是否单一,可能会有不同的判定结果。

如果出现以下情况,表示不满足单一职责原则:

  • 类中的代码行数,函数或者属性过多
  • 类依赖其他类过多, 或者依赖类的其他类过多
  • 私有方法过多
  • 比较难给类取一个合适的名字
  • 类中大量方法都集中在操作类的某些属性上
  • 代码中存在大量注释

开闭原则


如何理解 “对扩展开放,对修改关闭”

添加一个新功能,应该是通过现有共扩展代码(新增模块,类,方法,属性等), 而非修改已有代码的方式来完成。

  • 第一点,开闭原则并不少说完全杜绝修改,而是以最小的修改代码的代价完成新功能的开发。
  • 第二点,同样的代码修改,在粗粒度下,可以被认为是修改,在细粒度下,被认为是“扩展”。

我们对一个类添加新的方法。添加方法相当于修改类,在类这个层面,这个代码改动可以被认定为“修改”;但这个代码改动并没有修改已有的属性和方法,在方法(及其属性)这一层面,它又可以被认定为“扩展”


如何做到修改关闭,扩展开放?

时刻具备扩展意识,抽象意识,封装意识。编码时,要多花时间去思考,代码未来可能哪些需求变更,如何设计代码结构,事先留好扩展点,以便在未来在需求变更时,在不调整代码结构的基础上,做到最小代码的修改,将新代码灵活的放到扩展点上。

最常用的提高代码扩展的方法有:多态,依赖注入, 基于接口而非实现编程,与大部分设计模式。


里式替换

里式替换原则的英文翻译是:Liskov Substitution Principle,缩写为 LSP。这个原则最早是在 1986 年由 Barbara Liskov 提出,他是这么描述这条原则的:If S is a subtype of T, then objects of type T may be replaced with objects of type S, without breaking the program。

子类对象能给替换程序中父类出现的任何对方,并且保证程序的行为逻辑及正确性不被破坏。

多态是面向对象编程等一大特性,也是面向对象编程语言的一种语法,它是一种代码实现的思路,里式转换原则设计中,是用来指导继承关系中子类如何设计,子类的设计保证在替换父类时,不改变原有逻辑和程序的正确性。


接口隔离


接口隔离原则的英文翻译是“ Interface Segregation Principle”,缩写为 ISP。Robert Martin 在 SOLID 原则中是这样定义它的:“Clients should not be forced to depend upon interfaces that they do not use。”直译成中文的话就是:客户端不应该被强迫依赖它不需要的接口。其中的“客户端”,可以理解为接口的调用者或者使用者。

理解接口隔离重要是理解其中接口两字。接口理解一组接口集合,可以是某个服务的接口,可以是某个类的接口,如果部分接口被有这使用。


接口隔离原则和单一职责原则的区别

单一职责是针对的是类,模块,接口的设计,接口隔离原则相对单一原则更侧重于接口的设计。另外思考角度也不一样。接口隔离原则提供了一种判断接口是否单一的标准:通过调用者如何使用接口来判断,如果调用者使用部分接口或接口的部分功能,那接口的设计就不够单一

依赖倒置原则


控制反转

实际上,控制反转是一个比较笼统的设计思想,并不是一种具体的实现方法,一般用来指导框架层面的设计。这里所说的“控制”指的是对程序执行流程的控制,而“反转”指的是在没有使用框架之前,程序员自己控制整个程序的执行。在使用框架之后,整个程序的执行流程通过框架来控制。流程的控制权从程序员“反转”给了框架。


依赖注入

依赖注入和控制反转恰恰相反,它是一种具体的编码技巧。我们不通过 new 的方式在类内部创建依赖类的对象,而是将依赖的类对象在外部创建好之后,通过构造函数、函数参数等方式传递(或注入)给类来使用。依赖注入也是实现组合最简单的方式

type A struct {}
func NewA() A {
    return A{}
}
type B struct {
    a A
}
func NewB(a A) B {
    return B{a:a}
}
func main() {
    a := NewA()
    // 依赖注入
    b := NewB(a)
}

依赖反转原则也叫作依赖倒置原则。这条原则跟控制反转有点类似,主要用来指导框架层面的设计。高层模块不依赖低层模块,它们共同依赖同一个抽象。抽象不要依赖具体实现细节,具体实现细节依赖抽象。


KISS 原则


  • Keep It Simple and Stupid.
  • Keep It Short and Simple.
  • Keep It Simple and Straightforward.

其实主要含义是尽量保持简单

如何写出 KISS 原则的代码


  • 不要使用同事可能不懂的技术来实现代码
  • 不要重复造轮子,要善于使用已经有的工具类库
  • 不要过度优化

DRY 原则


DRY 原则(Don’t Repeat Yourself)几乎人尽皆知。你可能会觉得,这条原则非常简单、非常容易应用。只要两段代码长得一样,那就是违反 DRY 原则了。真的是这样吗?答案是否定的。这是很多人对这条原则存在的误解。实际上,重复的代码不一定违反 DRY 原则,而且有些看似不重复的代码也有可能违反 DRY 原则。

通常存在三种典型的代码重复情况,它们分别是:实现逻辑重复、功能语义重复和代码执行重复

问题:如何提高代码复用性?

  • 减少代码耦合
  • 满足单一职责原则
  • 模块化业务与非业务逻辑分离
  • 通用代码下沉
  • 继承、多态、抽象、封装
  • 应用模板等设计模式

LOD 原则

迪米特法则的英文翻译是:Law of Demeter,缩写是 LOD。单从这个名字上来看,我们完全猜不出这个原则讲的是什么。不过,它还有另外一个更加达意的名字,叫作最小知识原则,英文翻译为:The Least Knowledge Principle。

迪米特法则法则强调不该有直接依赖关系的类之间,不要有依赖;有依赖关系的类之间,尽量只依赖必要的接口。迪米特法则是希望减少类之间的耦合,让类越独立越好。每个类都应该少了解系统的其他部分。一旦发生变化,需要了解这一变化的类就会比较少。

“单一职责原则”、“接口隔离原则”以及“最小知识原则”,都是实现高内聚低耦合的有效指导思想。“最小知识原则”更强调类与类之间的关系。


相关文章
|
5月前
|
设计模式 网络协议 测试技术
你的代码是否按照高内聚、低耦合的原则来设计的?
你的代码是否按照高内聚、低耦合的原则来设计的?
|
9月前
|
数据库
软件设计原则
软件设计原则
|
9月前
|
设计模式 算法
软件设计的原则
软件设计的原则
50 0
|
9月前
|
设计模式 算法
原则的重要性(单一职责原则-开放封闭原则)一
原则的重要性(单一职责原则-开放封闭原则)一
65 0
|
10月前
|
设计模式 前端开发 Java
【Java设计模式 思想原则重构】设计思想、设计原则、重构总结
【Java设计模式 思想原则重构】设计思想、设计原则、重构总结
141 0
|
10月前
|
设计模式 安全 Java
软件设计原则有哪些(上)
软件设计原则有哪些(上)
86 0
|
10月前
|
设计模式
软件设计原则有哪些(下)
软件设计原则有哪些(下)
56 0
|
11月前
|
程序员 测试技术
面向对象设计五个基本原则
只有聪明人才能看见的简介~( ̄▽ ̄~)~
78 0
|
11月前
|
设计模式 测试技术 程序员
代码的简单设计五原则
代码的简单设计五原则
33028 1
|
设计模式 安全 Python
设计模式中应遵循的基本原则
设计模式中应遵循的基本原则
177 0