【注意】最后更新于 December 9, 2019,文中内容可能已过时,请谨慎使用。
今天继续打卡学习极客时间上的专栏“设计模式之美”,
本篇是专栏第16节的学习笔记,介绍面向对象设计原则中的开闭原则。
笔记
面向对象有很多经典的设计原则:
- SOLID5原则分别是
- 单一职责原则(SRP)、开闭原则(OCP)、里氏替换原则(LSP)、接口隔离原则(ISP)、依赖反转原则(DIP)
- KISS - Keep It Simple, Stupid
- YAGNI - You aren’t gonna need it
- DRY - Don’t Repeat Yourself
- LOD - Law of Demeter(迪米特法则,又叫做最少知识原则)
开闭原则
开闭原则(Open Closed Principle): 即软件实体(模块、类、方法等)对扩展开发,对修改关闭。
通俗的理解即添加一个新的功能应该是在已有代码基础上扩展代码(新增模块、类、方法等),而非修改已有代码(修改模块、类、方法等)。
在GOF 23种经典设计模式中,大部分设计模式是为了解决代码的扩展性问题而存在的,主要遵从的设计原则就是开闭原则。
开闭原则就是讲的代码的扩展性问题,如果某段代码在应对未来需求变化的时候,能够做到"对扩展开放、对修改关闭",那就说名这段代码的扩展性比较好。
为了尽量写出扩展性好的代码,要随时具有扩展意识、抽象意识、封装意识,在写代码的时候多思考,留好扩展点,但也不要过度设计,因为唯一不变的就是变化
。
另外,识别出代码可变部分和不可变部分,将可变部分封装起来,隔离变化,提供抽象化的不可变接口给上层系统使用。当具体实现发生变化时,只需基于相同的抽象接口,扩展一个新的实现,替换掉老的实现即可,而上游系统的代码几乎不需要修改。
在众多设计原则、设计思想、设计模式中,常用来提高代码扩展性的方法有: 多态、依赖注入、基于接口(而非实现)编程、使用大部分设计模式(如: 装饰、策略、模板、职责链、状态等)。实际上很多设计原则、设计思想、设计模式都是相通的。
另外,还要清楚开闭原则不是免费的,有些情况下代码的扩展性会跟可读性相冲突,应用设计模式很可能会引入过多的类以及复杂的关系,而且设计模式只是给我们的代码带来更好的扩展性而非性能。
代码重写
用Go语言中的接口型函数
重写文中的API接口监控告警示例
下面的代码采用基于接口(而非实现)编程
的设计思想,隔离了变化,同时借助Go语言中的接口型函数
特性,当有新的监控告警处理逻辑要加入时只需编写一个新的HandlerFunc
添加到其中即可。
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
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
|
package main
import (
"fmt"
)
type ApiStatInfo struct {
Api string
RequestCount int64
ErrorCount int64
DurationOfSeconds int64
}
type AlertHandler interface {
Check(apiStatInfo *ApiStatInfo) error
}
// 接口型函数
type AlertHandleFunc func(apiStatInfo *ApiStatInfo) error
func (f AlertHandleFunc) Check(apiStatInfo *ApiStatInfo) error {
return f(apiStatInfo)
}
type Alert struct {
AlertHandlers []AlertHandler
}
func NewAlert() *Alert {
return &Alert{AlertHandlers: []AlertHandler{}}
}
func (a *Alert) AddHandler(alertHandler AlertHandler) {
a.AlertHandlers = append(a.AlertHandlers, alertHandler)
}
func (a *Alert) AddHandlerFunc(f func(apiStatInfo *ApiStatInfo) error) {
a.AddHandler(AlertHandleFunc(f))
}
func (a *Alert) Check(apiStatInfo *ApiStatInfo) error {
for _, alertHandler := range a.AlertHandlers {
if err := alertHandler.Check(apiStatInfo); err != nil {
return err
}
}
return nil
}
func main() {
alert := NewAlert()
alert.AddHandlerFunc(HandleTpsAlert)
alert.AddHandlerFunc(HandleErrorAlert)
apiStatInfo := &ApiStatInfo{}
// ...省略对apiStatInfo属性赋值
alert.Check(apiStatInfo)
}
func HandleTpsAlert(apiStatInfo *ApiStatInfo) error {
// if ...
fmt.Println("hanlde tps alert")
return nil
}
func HandleErrorAlert(apiStatInfo *ApiStatInfo) error {
// if ...
fmt.Println("handle error alert")
return nil
}
|
个人理解
开闭原则是软件设计的核心原则,是大方向上的指导思想、指导方针。
在做软件设计时应始终以开闭原则为指导评审设计。
参考