设计模式之美学习笔记16: 设计原则之开闭原则(OCP)
2019-12-09
今天继续打卡学习极客时间上的专栏“设计模式之美”, 本篇是专栏第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
添加到其中即可。
1package main
2
3import (
4 "fmt"
5)
6
7type ApiStatInfo struct {
8 Api string
9 RequestCount int64
10 ErrorCount int64
11 DurationOfSeconds int64
12}
13
14type AlertHandler interface {
15 Check(apiStatInfo *ApiStatInfo) error
16}
17
18// 接口型函数
19type AlertHandleFunc func(apiStatInfo *ApiStatInfo) error
20func (f AlertHandleFunc) Check(apiStatInfo *ApiStatInfo) error {
21 return f(apiStatInfo)
22}
23
24type Alert struct {
25 AlertHandlers []AlertHandler
26}
27
28func NewAlert() *Alert {
29 return &Alert{AlertHandlers: []AlertHandler{}}
30}
31func (a *Alert) AddHandler(alertHandler AlertHandler) {
32 a.AlertHandlers = append(a.AlertHandlers, alertHandler)
33}
34func (a *Alert) AddHandlerFunc(f func(apiStatInfo *ApiStatInfo) error) {
35 a.AddHandler(AlertHandleFunc(f))
36}
37func (a *Alert) Check(apiStatInfo *ApiStatInfo) error {
38 for _, alertHandler := range a.AlertHandlers {
39 if err := alertHandler.Check(apiStatInfo); err != nil {
40 return err
41 }
42 }
43 return nil
44}
45
46func main() {
47 alert := NewAlert()
48 alert.AddHandlerFunc(HandleTpsAlert)
49 alert.AddHandlerFunc(HandleErrorAlert)
50 apiStatInfo := &ApiStatInfo{}
51 // ...省略对apiStatInfo属性赋值
52 alert.Check(apiStatInfo)
53}
54
55
56func HandleTpsAlert(apiStatInfo *ApiStatInfo) error {
57 // if ...
58 fmt.Println("hanlde tps alert")
59 return nil
60}
61func HandleErrorAlert(apiStatInfo *ApiStatInfo) error {
62 // if ...
63 fmt.Println("handle error alert")
64 return nil
65}
个人理解 #
开闭原则是软件设计的核心原则,是大方向上的指导思想、指导方针。 在做软件设计时应始终以开闭原则为指导评审设计。