设计模式6原则之:单一职责原则
概念
一个类或者模块只负责完成一个职责(或者功能)
理解
- 不要设计大而全的类,要设计粒度小、功能单一的类
- 一个类包含了两个或者两个以上业务不相干的功能,那我们就说它职责不够单一,应该将它拆分成多个功能更加单一、粒度更细的类
如何判断?
-
类中的代码行数、函数或属性过多,会影响代码的可读性和可维护性,我们就需要考虑对类进行拆分
像四五千行的代码就可以考虑拆分了。。。一般来说一个类几百行代码算合理的,类函数和属性最好不要超过 20 个
-
类依赖的其他类过多,或者依赖类的其他类过多,不符合高内聚、低耦合的设计思想,我们就需要考虑对类进行拆分
-
私有方法过多,我们就要考虑能否将私有方法独立到新的类中,设置为 public 方法,供更多的类使用,从而提高代码的复用性
-
比较难给类起一个合适名字,很难用一个业务名词概括,或者只能用一些笼统的 Manager、Context 之类的词语来命名,这就说明类的职责定义得可能不够清晰
-
类中大量的方法都是集中操作类中的某几个属性,那就可以考虑将这几个属性和对应的方法拆分出来
方法单一
比如现在我们要新增或者更新小测验,会这么写
func (qz *Quiz) SaveQuiz(params map[string]string) {
if id, ok := params["id"]; ok {
fmt.Println("更新小测验", id)
} else {
fmt.Println("保存小测验", id)
}
}
如果 id 存在就走更新分支,否则新增。之所以这么写是因为更新和新增的逻辑会有不同的地方,比如更新可能会走后台线程打包成课件包,新增不会。这样放在一个方法里面会让代码变得复杂而模糊,让后期维护也变得复杂,不符合高内聚低耦合的思想。
这时候就要考虑拆分成 2 个方法:
package quizservice
import "fmt"
type IQuiz interface {
SaveQuiz(params map[string]string)
UpdateQuiz(params map[string]string)
}
type Quiz struct {
}
func (qz *Quiz) SaveQuiz(params map[string]string) {
fmt.Println("添加小测验")
}
func (qz *Quiz) UpdateQuiz(params map[string]string) {
fmt.Println("更新小测验")
}
接口单一
比如添加一些课件小节,包括视频小节,文档小节。其中视频小节包括基础模块、视频模块;文档小节包括基础模块、文档模块。
type IChapter interface {
AddBaseModule()
AddVideoModule()
AddDocumentModule()
}
type DocumentChapter struct {
}
func (d *DocumentChapter) AddBaseModule() {
fmt.Println("组装基础模块")
}
func (d *DocumentChapter) AddVideoModule() {
fmt.Println("不需要组装...")
}
func (d *DocumentChapter) AddDocumentModule() {
fmt.Println("组装文档模块")
}
type VideoChapter struct {
}
func (d *VideoChapter) AddBaseModule() {
fmt.Println("组装基础模块")
}
func (d *VideoChapter) AddVideoModule() {
fmt.Println("组装视频模块")
}
func (d *VideoChapter) AddDocumentModule() {
fmt.Println("不需要组装...")
}
像上面这样都实现了接口方法,但是文档不需要组装视频模块,视频也不需要组装文档模块,这样就会导致接口的职责不够单一。
修改如下:
type IChapter interface {
AddBaseModule()
}
type IDocument interface {
IChapter
AddDocumentModule()
}
type IVideo interface {
IChapter
AddVideoModule()
}
type DocumentChapter struct {
}
func (d *DocumentChapter) AddBaseModule() {
fmt.Println("组装基础模块")
}
func (d *DocumentChapter) AddDocumentModule() {
fmt.Println("组装文档模块")
}
type VideoChapter struct {
}
func (d *VideoChapter) AddBaseModule() {
fmt.Println("组装基础模块")
}
func (d *VideoChapter) AddVideoModule() {
fmt.Println("组装视频模块")
}