【无标题】

1 篇文章 0 订阅
1 篇文章 0 订阅

Go中interface编程思想

理解interface

  • interface是方法声明的集合
  • 任何类型的对象实现了interface接口中声明的全部方法,则表明该类型实现了接口
  • interface可以作为一种数据类型,实现该接口的任何对象都可以对对应接口类型变量赋值。

开闭原则

interface的意义其实是为了满足软件设计的高内聚、低耦合思想。那这其中就需要满意一些原则,如:开闭原则
举例:

package main

import "fmt"

type Banker struct {
}

// Save 存款业务
func (this *Banker) Save() {
	fmt.Println("进行了 存款业务...")
}

// Transfer 转账业务
func (this *Banker) Transfer() {
	fmt.Println("进行了 转账业务...")
}

// Pay 支付业务
func (this *Banker) Pay() {
	fmt.Println("进行了 支付业务...")
}

func main() {
	banker := &Banker{}

	banker.Save()
	banker.Pay()
	banker.Transfer()
}

上面的例子很简单,银行业务有:存款业务、转账业务、支付业务。但是后面会增加很多很多业务的时候,这里业务模块会爆炸。
每次新增业务都要这里添加业务,如果新加的业务崩溃也会导致前面业务一起挂掉。业务都在一个Banker中,耦合度太高,开发人员维护成本太高。

如何优化?
我们可以使用interface将业务抽象出来,制作一个抽象的Banker模块,然后提供一个抽象的方法。 分别根据这个抽象模块,
去实现支付Banker(实现支付方法),转账Banker(实现转账方法)。
当我们给一个系统添加一个功能的时候,不是通过修改代码,而是通过增添代码来完成,那么就是开闭原则的核心思想了。所以要想满足上面的要求,
是一定需要interface来提供一层抽象的接口的。
代码修改如下:


func main() {
	/*	banker := &Banker{}

		banker.Save()
		banker.Pay()
		banker.Transfer()*/

	/*//进行存款
	sb := &SaveBanker{}
	sb.DoBusiness()

	//进行转账
	tb := &TransferBanker{}
	tb.DoBusiness()

	//进行支付
	pb := &PayBanker{}
	pb.DoBusiness()
	*/
	// 存款
	BankerBusiness(&SaveBanker{})

	// 转账
	BankerBusiness(&TransferBanker{})

	// 支付
	BankerBusiness(&PayBanker{})
}

// AbstractBanker 抽象银行业务
type AbstractBanker interface {
	DoBusiness()
}

// TransferBanker 转账业务员 进行转账业务
type TransferBanker struct {
}

func (tb *TransferBanker) DoBusiness() {
	fmt.Println("用户转账...")
}

// SaveBanker 存库业务员 进行存库业务
type SaveBanker struct {
}

func (sb *SaveBanker) DoBusiness() {
	fmt.Println("用户存款...")
}

// PayBanker 支付的业务员
type PayBanker struct {
	//AbstractBanker
}

func (pb *PayBanker) DoBusiness() {
	fmt.Println("进行支付")
}

// BankerBusiness 实现架构层(基于抽象层进行业务封装-针对interface接口进行封装)
func BankerBusiness(banker AbstractBanker) {
	//通过接口来向下调用,(多态现象)
	banker.DoBusiness()
}

依赖倒置

在软件设计的时候,一般为了业务功能扩展性会将模块分为3个层次,抽象层、实现层、业务逻辑层。
如何做?
先将抽象层的模块和接口定义出来, 这里就需要interface接口的设计,然后我们依照抽象层,依次实现每个实现层的模块,在我们写实现层代码的时候,实际上我们
只需要参考对应的抽象层实现就好了,实现每个模块,也和其他的实现的模块没有关系,这样也符合了上面介绍的开闭原则。这样实
现起来每个模块只依赖对象的接口,而和其他模块没关系,依赖关系单一。
指定业务逻辑也是一样,只需要参考抽象层的接口来业务就好了,抽象层暴露出来的接口就是我们业务层可以使用的方法,然后
可以通过多态的线下,接口指针指向哪个实现模块,调用了就是具体的实现方法,这样我们业务逻辑层也是依赖抽象成编程。
这种的设计原则就叫做依赖倒转原则。
示例:
组装2台电脑,
— 抽象层 —有显卡Card 方法display,有内存Memory 方法storage,有处理器CPU 方法calculate
— 实现层层 —有 Intel因特尔公司 、产品有(显卡、内存、CPU),有 Kingston 公司, 产品有(内存3),有 NVIDIA 公司, 产品有(显卡)
— 逻辑层 —1. 组装一台Intel系列的电脑,并运行,2. 组装一台 Intel CPU Kingston内存 NVIDIA显卡的电脑,并运行

package main

import "fmt"

type Card interface {
	Display()
}

type Memory interface {
	Storage()
}

type CPU interface {
	Calculate()
}

type Computer struct {
	cpu    CPU
	card   Card
	memory Memory
}

func NewComputer(cpu CPU, card Card, memory Memory) *Computer {
	return &Computer{
		cpu:    cpu,
		card:   card,
		memory: memory,
	}
}

func (c *Computer) DoWork() {
	c.cpu.Calculate()
	c.card.Display()
	c.memory.Storage()
}

// 实现层
type IntelCard struct {
	Card
}

func (intel *IntelCard) Display() {
	fmt.Println("intel card 显示内容了。。。")
}

type IntelMemory struct {
	Memory
}

func (intel *IntelMemory) Storage() {
	fmt.Println("intel memory storage ...")
}

type IntelCpu struct {
	CPU
}

func (intel *IntelCpu) Calculate() {
	fmt.Println("intel calculate 计算内容...")
}

type KingstonMemory struct {
	Memory
}

func (k *KingstonMemory) Storage() {
	fmt.Println("Kingston's memory (内存3) storage ...")
}

type NVIDIACard struct {
	Card
}

func (n *NVIDIACard) Display() {
	fmt.Println("NVIDIA card 显示内容了。。。")
}

// 逻辑层
func main() {
	//intel系列的电脑
	com1 := NewComputer(&IntelCpu{}, &IntelCard{}, &IntelMemory{})
	com1.DoWork()
	// 其他牌子
	com2 := NewComputer(&IntelCpu{}, &NVIDIACard{}, &KingstonMemory{})
	com2.DoWork()
}

参考

  • https://www.yuque.com/aceld/golang/uh0124
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值