第4节 依赖倒转原则


❤️💕💕Java和Golang的设计模式,设计模式介绍、创建者模式、结构型模式、行为型模式。Myblog:http://nsddd.topopen in new window


[TOC]

原则

警告

(Dependence Inversion Principle, DIP)

依赖于抽象(接口),不要依赖具体的实现(类),也就是针对接口编程。

没有使用依赖倒转

// 依赖倒转原则
package main

import "fmt"

//+司机张三、李四、王五
//+++张三开奔驰
//+++李四开宝马

//张三奔驰
type Zhangsan struct {
}

//李四宝马
type Lisi struct {
}

//张三开奔驰
func (b *Zhangsan) Run() {
	fmt.Println("奔驰在跑")
}

//奔驰
type Benz struct {
}

//宝马
type Bmw struct {
}

//奔驰
func (b *Benz) Run() {
	fmt.Println("奔驰在跑")
}

//宝马
func (b *Lisi) Run() {
	fmt.Println("宝马在跑")
}

//张三想开宝马
func (b *Zhangsan) WantRun() {
	fmt.Println("张三想开宝马")
}

//李四想开奔驰
func (b *Lisi) WantRun() {
	fmt.Println("李四想开奔驰")
}

func main() {
	//张三开奔驰
	zhangsan := &Zhangsan{}
	zhangsan.Run()
	//李四开宝马
	lisi := &Lisi{}
	lisi.Run()

	//张三想开宝马
	zhangsan.WantRun()
	//李四想开奔驰
	lisi.WantRun()
}

🚀 编译结果如下:

[Running] go run "d:\文档\最近的\awesome-golang\docs\code\go-super\81-main.go"
奔驰在跑
宝马在跑
张三想开宝马
李四想开奔驰

📜 对上面的解释

我们可以看出这样设计的弊端,我们希望张三开奔驰,李四想开奔驰的时候,需要额外加方法。

image-20221128112622244

我们应该尽量降低耦合度的发生

我们来看上面的代码和图中每个模块之间的依赖关系,实际上并没有用到任何的interface接口层的代码,显然最后我们的两个业务 张三开奔驰, 李四开宝马,程序中也都实现了。但是这种设计的问题就在于,小规模没什么问题,但是一旦程序需要扩展,比如我现在要增加一个丰田汽车 或者 司机王五, 那么模块和模块的依赖关系将成指数级递增,想蜘蛛网一样越来越难维护和捋顺。

使用依赖倒转

image-20221128113217815

注意

我们需要记住需要引入抽象层,接口其实最大的目的也是为了实现抽象,对接业务层和实现层。

我们依照抽象层,依次实现每个实现层的模块,在我们写实现层代码的时候,实际上我们只需要参考对应的抽象层实现就好了,实现每个模块,也和其他的实现的模块没有关系,这样也符合了上面介绍的开闭原则。这样实现起来每个模块只依赖对象的接口,而和其他模块没关系,依赖关系单一。系统容易扩展和维护。

我们在指定业务逻辑也是一样,只需要参考抽象层的接口来业务就好了,抽象层暴露出来的接口就是我们业务层可以使用的方法,然后可以通过多态的线下,接口指针指向哪个实现模块,调用了就是具体的实现方法,这样我们业务逻辑层也是依赖抽象成编程。

💡简单的一个案例如下:

package main

import "fmt"

//====> abstract layer <========
type Car interface {
	Run()
}

type Driver interface {
	Drive(car Car)
}

//====> Implementation layer <========
type BenZ struct {
	//...
}

func (benz *BenZ) Run() {
	fmt.Println("Benz is running...")
}

type Bmw struct {
	//...
}

func (bmw *Bmw) Run() {
	fmt.Println("Bmw is running...")
}

type Zhang_3 struct {
	//...
}

func (zhang3 *Zhang_3) Drive(car Car) {
	fmt.Println("Zhang3 drive -->", car)
	car.Run()
}

type Li_4 struct {
	//...
}

func (li4 *Li_4) Drive(car Car) {
	fmt.Println("li4 drive -->", car)
	car.Run()
}

type Wangwu struct {
	//...
}

func (wangwu *Wangwu) Drive(car Car) {
	fmt.Println("wangwu drive -->", car)
	car.Run()
}

//====> Business logic layer <=========
func main() {
	//Zhang 3 drives BMW
	var bmw Car = &Bmw{}

	//In 4 Mercedes drives
	var benz Car = &BenZ{}

	var zhang3 Driver = &Zhang_3{}
	zhang3.Drive(bmw)

	//Zhang San wants to drive a BMW
	zhang3.Drive(benz)

	var li4 Driver
	li4 = &Li_4{}
	li4.Drive(benz)

	//Li si wants to drive Mercedes
	li4.Drive(bmw)

	var wangwu Driver = &Wangwu{}
	wangwu.Drive(benz)
	wangwu.Drive(bmw)
}

🚀 编译结果如下:

[Running] go run "d:\文档\最近的\awesome-golang\docs\code\go-super\82-main.go"
Zhang3 drive --> &{}
Bmw is running...
Zhang3 drive --> &{}
Benz is running...
li4 drive --> &{}
Benz is running...
li4 drive --> &{}
Bmw is running...
wangwu drive --> &{}
Benz is running...
wangwu drive --> &{}
Bmw is running...

END 链接