缘起

最近复习设计模式
拜读谭勇德的<<设计模式就该这样学>>
该书以java语言演绎了常见设计模式
本系列笔记拟采用golang练习之

依赖倒置原则

  • 依赖倒置原则(Dependence Inversion Principle, DIP)指设计代码结构时,高层模块不应该依赖底层模块,二者都应该依赖其抽象。
  • 抽象不应该依赖细节,细节应该依赖抽象。
  • 常见的依赖注入方式有: 方法参数注入, 构造器参数注入, setter方法注入

_

场景

  • 线上学校有一系列课程
  • 用户可选择若干门课程进行学习
  • 如果把学习课程的过程直接实现为用户的方法, 则每增加一门课程, 就需要增加一个学习方法
  • 根据依赖倒置原则, 可以把学习过程抽象为学习接口, 由不同的课程实例各自实现

BadUser.go

不好的示例: BadUser以不同方法实现各种课程的学习过程, 课程的增加导致BadUser代码越来越臃肿

  1. package dependence_inversion
  2. import "fmt"
  3. type BadUser struct {
  4. iID int
  5. sName string
  6. }
  7. func NewBadUser(id int, name string) *BadUser {
  8. return &BadUser{
  9. iID: id,
  10. sName: name,
  11. }
  12. }
  13. func (me *BadUser) StudyJavaCourse() {
  14. fmt.Printf("%v is learning %v\n", me.sName, "java")
  15. }
  16. func (me *BadUser) StudyGolangCourse() {
  17. fmt.Printf("%v is learning %v\n", me.sName, "golang")
  18. }

GoodUser.go

GoodUser通过实现IUser接口提供用户基本信息, 并把不同课程的学习过程, 委托给ICourse接口去实现

  1. package dependence_inversion
  2. type IUser interface {
  3. ID() int
  4. Name() string
  5. Study(ICourse)
  6. }
  7. type GoodUser struct {
  8. iID int
  9. sName string
  10. }
  11. func NewGoodUser(id int, name string) IUser {
  12. return &GoodUser{
  13. iID: id,
  14. sName: name,
  15. }
  16. }
  17. func (me *GoodUser) ID() int {
  18. return me.iID
  19. }
  20. func (me *GoodUser) Name() string {
  21. return me.sName
  22. }
  23. func (me *GoodUser) Study(course ICourse) {
  24. course.SetUser(me)
  25. course.Study()
  26. }

GolangCourse.go

通过setter方法注入IUser, ICourse接口封装了具体课程的学习过程

  1. package dependence_inversion
  2. import "fmt"
  3. type ICourse interface {
  4. ID() int
  5. Name() string
  6. SetUser(IUser)
  7. Study()
  8. }
  9. type GolangCourse struct {
  10. iID int
  11. sName string
  12. xCurrentUser IUser
  13. }
  14. func NewGolangCourse() ICourse {
  15. return &GolangCourse{
  16. iID: 11,
  17. sName: "golang",
  18. xCurrentUser: nil,
  19. }
  20. }
  21. func (me *GolangCourse) ID() int {
  22. return me.iID
  23. }
  24. func (me *GolangCourse) Name() string {
  25. return me.sName
  26. }
  27. func (me *GolangCourse) SetUser(user IUser) {
  28. me.xCurrentUser = user
  29. }
  30. func (me *GolangCourse) Study() {
  31. fmt.Printf("%v is learning %v\n", me.xCurrentUser.Name(), me.Name())
  32. }

dependence_inversion_test.go

测试用例

  1. package main
  2. import "testing"
  3. import (
  4. dip "learning/gooop/principles/dependence_inversion"
  5. )
  6. func TestDIP(t *testing.T) {
  7. bu := dip.NewBadUser(1, "Tom")
  8. bu.StudyGolangCourse()
  9. gu := dip.NewGoodUser(2, "Mike")
  10. gu.Study(dip.NewGolangCourse())
  11. }

测试输出

  1. $ go test -v main/dependence_inversion_test.go
  2. === RUN TestDIP
  3. Tom is learning golang
  4. Mike is learning golang
  5. --- PASS: TestDIP (0.00s)
  6. PASS
  7. ok command-line-arguments 0.002s