【笔记】Go语言面向对象设计原则

前言

Go语言面向对象设计原则学习笔记
中心思想:高内聚、低耦合

单一指责原则(Single Responsibility Principle, SRP)

  • 类的职责单一,对外只提供一种功能,而引起类变化的原因都应该只有一个

采用前代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
package main

// Clothes 穿衣服的方式
type Clothes struct {
}

// OnWork 工作需要穿的衣服
func (c *Clothes) OnWork() {}

// OnShop 逛街需要穿的衣服
func (c *Clothes) OnShop() {}

func main() {
var c = Clothes{}

// 工作时穿的衣服
c.OnWork()

// 逛街时穿的衣服
c.OnShop()
}

采用后代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
package main

// ClothesForWork 工作需要穿的衣服
type ClothesForWork struct {
}

func (c *ClothesForWork) OnStyle() {}

// ClothesForShop 逛街需要穿的衣服
type ClothesForShop struct {
}

func (c *ClothesForShop) OnStyle() {}

func main() {

// 工作时穿的衣服
var clothesForWork = ClothesForWork{}
clothesForWork.OnStyle()

// 逛街时穿的衣服
var clothesForShop = ClothesForShop{}
clothesForShop.OnStyle()
}

开闭原则(Open-Closed Principle, OCP)

  • 类的改动是通过增加代码进行的,而不是修改代码

采用前代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
package main

// Banker 银行业务员
type Banker struct{}

// Save 存款业务
func (b *Banker) Save() {}

// Pay 支付业务
func (b *Banker) Pay() {}

func main() {
var banker = Banker{}

// 执行存款业务
banker.Save()

// 执行支付业务
banker.Pay()
}

采用后代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
package main

// Banker 银行业务员
type Banker interface {
DoBusiness()
}

// BankerSaveImpl 存款业务员
type BankerSaveImpl struct{}

func (b *BankerSaveImpl) DoBusiness() {}

// BankerPayImpl 支付业务员
type BankerPayImpl struct{}

func (b *BankerPayImpl) DoBusiness() {}

func main() {

// 执行存款业务
var bankerSaveImpl Banker = &BankerSaveImpl{}
bankerSaveImpl.DoBusiness()

// 执行支付业务
var bankerPayImpl Banker = &BankerPayImpl{}
bankerPayImpl.DoBusiness()
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
package main

// Banker 银行业务员
type Banker interface {
DoBusiness()
}

// BankerBusiness 银行业务执行
func BankerBusiness(b Banker) {
b.DoBusiness()
}

// BankerSaveImpl 存款业务员
type BankerSaveImpl struct{}

func (b *BankerSaveImpl) DoBusiness() {}

// BankerPayImpl 支付业务员
type BankerPayImpl struct{}

func (b *BankerPayImpl) DoBusiness() {}

func main() {

// 执行存款业务
BankerBusiness(&BankerSaveImpl{})

// 执行支付业务
BankerBusiness(&BankerPayImpl{})
}

里氏代换原则(Liskov Substitution Principle, LSP)

  • 任何抽象类(接口)出现的地方都可以用他的实现类进行替换,实际就是虚拟机制,通过语言级别实现面向对象功能

依赖倒转原则(Dependence Inversion Principle, DIP)

  • 面向接口编程
  • 依赖于抽象类(接口),不依赖于具体实现类

采用前代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
package main

// Benz 奔驰汽车
type Benz struct{}

// BMW 宝马汽车
type BMW struct{}

// ZhangSan 司机张三
type ZhangSan struct{}

// DriveBenz 开奔驰
func (zs *ZhangSan) DriveBenz() {}

// DriveBMW 开宝马
func (zs *ZhangSan) DriveBMW() {}

// LiSi 司机李四
type LiSi struct{}

// DriveBenz 开奔驰
func (ls *LiSi) DriveBenz() {}

// DriveBMW 开宝马
func (ls *LiSi) DriveBMW() {}

func main() {
var zs = ZhangSan{}
var ls = LiSi{}

// 张三开奔驰
zs.DriveBenz()
// 张三开宝马
zs.DriveBMW()

// 李四开奔驰
ls.DriveBenz()
// 李四开宝马
ls.DriveBMW()
}

采用后代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
package main

// Car 汽车
type Car interface {
Run()
}

// Driver 司机
type Driver interface {
Drive(car Car)
}

// Benz 奔驰汽车
type Benz struct{}

func (benz *Benz) Run() {}

// BMW 宝马汽车
type BMW struct{}

func (bmw *BMW) Run() {}

// ZhangSan 司机张三
type ZhangSan struct{}

func (zs *ZhangSan) Drive(car Car) {
car.Run()
}

// LiSi 司机李四
type LiSi struct{}

func (ls *LiSi) Drive(car Car) {
car.Run()
}

func main() {
var zs Driver = new(ZhangSan)
var ls Driver = new(LiSi)

// 张三开奔驰
zs.Drive(&Benz{})
// 张三开宝马
zs.Drive(&BMW{})

// 李四开奔驰
ls.Drive(&Benz{})
// 李四开宝马
ls.Drive(&BMW{})
}

接口隔离原则(Interface Segregation Principle, ISP)

  • 不应该强迫用户的程序依赖他们不需要的方法,一个接口应该只提供一种对外功能,不应该把所有操作都封装到一个接口中去

合成复用原则(Composite Reuse Principle, CRP)

  • 如果使用继承,会导致父类的任何变换都可能影响到子类的行为,如果使用对象组合,就降低了这种依赖关系,对于继承和组合,优先使用组合

采用前代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
package main

// Cat 猫
type Cat struct{}

// Eat 吃
func (c *Cat) Eat() {}

// Sleep 睡
func (c *Cat) Sleep() {}

// CatNew 新猫
type CatNew struct {
Cat
}

func main() {
var catNew = CatNew{}
catNew.Eat()
catNew.Sleep()
}

采用后代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
package main

// Cat 猫
type Cat struct{}

// Eat 吃
func (c *Cat) Eat() {}

// Sleep 睡
func (c *Cat) Sleep() {}

// CatNew 新猫
type CatNew struct {
Cat Cat
}

// Eat 吃
func (cn *CatNew) Eat() {
cn.Cat.Eat()
}

// Sleep 睡
func (cn *CatNew) Sleep() {}

func main() {
var catNew = CatNew{}
catNew.Eat()
catNew.Sleep()
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
package main

// Cat 猫
type Cat struct{}

// Eat 吃
func (c *Cat) Eat() {}

// Sleep 睡
func (c *Cat) Sleep() {}

// CatNew 新猫
type CatNew struct {}

// Eat 吃
func (cn *CatNew) Eat(cat Cat) {
cat.Eat()
}

// Sleep 睡
func (cn *CatNew) Sleep() {}

func main() {
var catNew = CatNew{}
catNew.Eat(Cat{})
catNew.Sleep()
}

迪米特法则(Law of Demeter, LoD)

  • 一个对象应该对其他对象了解越少越好,从而降低对象之间的耦合

完成

参考文献

哔哩哔哩——刘丹冰AceId