缘起
最近复习设计模式
拜读谭勇德的<<设计模式就该这样学>>
该书以java语言演绎了常见设计模式
本系列笔记拟采用golang练习之
单一职责原则
- 单一职责原则(Simple Responsibility Principle, SRP)指不要存在一个以上导致类变更的原因。假设有一个Class负责两个职责,一旦发生需求变更,修改其中一个职责的逻辑代码,有可能会导致另一个职责的功能发生故障。
- 分别用两个Class来实现两个职责,进行解耦。总体来说就是一个Class、Interface、Method只负责一项职责。
场景
- 某线上学院提供直播课和录播课两种产品
- 直播课可以播放和暂停, 不支持快进快退
- 录播课支持播放, 暂停, 快进和快退
- 如果把直播课和录播课实现在一个class里面, 则快进和快退的处理会比较麻烦
- 将直播课和录播课分开class实现, 从而遵循单一职责原则
BadCourse.go
不好的示例: 把两种课程的处理放在一个class, BadCourse承担了多种职责.
package simple_responsibilityimport "fmt"type IBadCourse interface {ID() intName() stringPlay()Pause()Forward(int)Backward(int)}type BadCourse struct {iID intsName string}func NewBadCourse(id int, name string) *BadCourse {return &BadCourse{iID: id,sName: name,}}func (me *BadCourse) ID() int {return me.iID}func (me *BadCourse) Name() string {return me.sName}func (me *BadCourse) Play() {fmt.Printf("%v play\n", me.Name())}func (me *BadCourse) Pause() {fmt.Printf("%v pause\n", me.Name())}func (me *BadCourse) Forward(seconds int) {if me.Name() == "录播课" {fmt.Printf("%v forward %v seconds\n", me.Name(), seconds)} else {fmt.Printf("%v cannot forward\n", me.Name())}}func (me *BadCourse) Backward(seconds int) {if me.Name() == "录播课" {fmt.Printf("%v backward %v seconds\n", me.Name(), seconds)} else {fmt.Printf("%v cannot forward\n", me.Name())}}
GoodCourse.go
更好的示例, 定义课程接口和课程控制接口
package simple_responsibilitytype IGoodCourse interface {ID() intName() stringController() IPlayControl}type IPlayControl interface {Play()Pause()}type IReplayControl interface {IPlayControlForward(seconds int)Backward(seconds int)}type CourseInfo struct {iID intsName string}func (me *CourseInfo) ID() int {return me.iID}func (me *CourseInfo) Name() string {return me.sName}
LiveCourse.go
更好的示例, 直播课的实现.
LiveCourse通过集成CourseInfo实现IGoodCourse接口, 同时实现了IPlayControl接口.
package simple_responsibilityimport ("fmt")type LiveCourse struct {CourseInfo}func NewLiveCourse(id int, name string) IGoodCourse {return &LiveCourse{CourseInfo{iID: id,sName: name,},}}func (me *LiveCourse) Controller() IPlayControl {return me}func (me *LiveCourse) Play() {fmt.Printf("%v play\n", me.Name())}func (me *LiveCourse) Pause() {fmt.Printf("%v pause\n", me.Name())}
ReplayCourse.go
更好的示例, 录播课的实现.
ReplayCourse通过集成CourseInfo实现IGoodCourse接口, 同时实现了IReplayControl接口
package simple_responsibilityimport ("fmt")type ReplayCourse struct {CourseInfo}func NewReplayCourse(id int, name string) IGoodCourse {return &ReplayCourse{CourseInfo{iID: id,sName: name,},}}func (me *ReplayCourse) Controller() IPlayControl {return me}func (me *ReplayCourse) Play() {fmt.Printf("%v play\n", me.Name())}func (me *ReplayCourse) Pause() {fmt.Printf("%v pause\n", me.Name())}func (me *ReplayCourse) Forward(seconds int) {fmt.Printf("%v forward %v\n", me.Name(), seconds)}func (me *ReplayCourse) Backward(seconds int) {fmt.Printf("%v backward %v\n", me.Name(), seconds)}
simple_responsibility_test.go
单元测试
package mainimport ("learning/gooop/principles/simple_responsibility""testing")func Test_SimpleResponsibility(t *testing.T) {fnTestBadCourse := func(bc *simple_responsibility.BadCourse) {bc.Play()bc.Pause()bc.Forward(30)bc.Backward(30)}fnTestBadCourse( simple_responsibility.NewBadCourse(1, "直播课"))fnTestBadCourse( simple_responsibility.NewBadCourse(2, "录播课"))fnTestGoodCourse := func(gc simple_responsibility.IGoodCourse) {pc := gc.Controller()pc.Play()pc.Pause()if rc, ok := pc.(simple_responsibility.IReplayControl);ok {rc.Forward(30)rc.Backward(30)}}fnTestGoodCourse(simple_responsibility.NewLiveCourse(11, "直播课"))fnTestGoodCourse(simple_responsibility.NewReplayCourse(12, "录播课"))}
测试输出
$ go test -v simple_responsibility_test.go=== RUN Test_SimpleResponsibility直播课 play直播课 pause直播课 cannot forward直播课 cannot forward录播课 play录播课 pause录播课 forward 30 seconds录播课 backward 30 seconds直播课 play直播课 pause录播课 play录播课 pause录播课 forward 30录播课 backward 30--- PASS: Test_SimpleResponsibility (0.00s)PASSok command-line-arguments 0.003s
