依赖倒置原则 (Dependence Inversion Principle, DIP)

指设计代码结构时:

  • 高层模块不应该依赖底层模块,二者都应该依赖其抽象。
  • 抽象不应该依赖细节,细节应该依赖抽象。
  • 常见的依赖注入方式有:构造器参数注入,setter 方法注入。

场景:

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

BadUser

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

package dependence

import "fmt"

type BadUser struct {
	iID int
	sName string
}

func NewBadUser(id int, name string) *BadUser {
	return &BadUser{
		iID: id,
		sName: name,
	}
}

func (me *BadUser) StudyJavaCourse() {
	fmt.Printf("%v is learning %v\n", me.sName, "java")
}

func (me *BadUser) StudyGolangCourse() {
	fmt.Printf("%v is learning %v\n", me.sName, "golang")
}

GoodUser

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

package dependence

import (
	"fmt"
)

type IUser interface {
	ID() int
	Name() string
	Study(ICourse)
}

type GoodUser struct {
	iID int
	sName string
}

func NewGoodUser(id int, name string) IUser {
	return &GoodUser{
		iID: id,
		sName: name,
	}
}

func (me *GoodUser) ID() int {
	return me.iID
}

func (me *GoodUser) Name() string {
	return me.sName
}

func (me *GoodUser) Study(course ICourse) {
	course.SetUser(me)
	course.Study()
}

GolangCourse

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

package dependence

import "fmt"

type ICourse interface {
    ID() int
    Name() string
    SetUser(IUser)
    Study()
}

type GolangCourse struct {
    iID int
    sName string
    xCurrentUser IUser
}

func NewGolangCourse() ICourse {
    return &GolangCourse{
        iID: 11,
        sName: "golang",
        xCurrentUser: nil,
    }
}

func (me *GolangCourse) ID() int {
    return me.iID
}


func (me *GolangCourse) Name() string {
    return me.sName
}

func (me *GolangCourse) SetUser(user IUser) {
    me.xCurrentUser = user
}

func (me *GolangCourse) Study() {
    fmt.Printf("%v is learning %v\n", me.xCurrentUser.Name(), me.Name())
}

测试:

package main

import (
	"learning/dependence"
	"testing"
)


func TestDIP(t *testing.T) {
	bu := dependence.NewBadUser(1, "Tom")
	bu.StudyGolangCourse()

	gu := dependence.NewGoodUser(2, "Mike")
	gu.Study(dependence.NewGolangCourse())
}