为了账号安全,请及时绑定邮箱和手机立即绑定

设计 Go 程序以避免循环依赖

设计 Go 程序以避免循环依赖

Go
眼眸繁星 2023-06-01 16:28:39
我是 Golang 的新手,我做了一个学习它的例子,但是我的例子中不允许导入周期,所以有人知道如何避免这种情况吗?这是我的代码。银行,去package Bankimport (    "../../class/human"    "fmt")func Transfer(payer, receiver *human.Human, payment float64) {    if payer.Bank > payment {        payer.Bank -= payment        receiver.Bank += payment    } else {        fmt.Println("Bank balance not enough")    }}人间围棋package human// import "../../func/Bank"type Human struct {    Name string    Cash float64    Bank float64}func (h *Human) Transfer(Receiver Human, payment float64) {}主程序package mainimport (    "./class/human"    "./func/Bank")func main() {    gary := human.Human{"Gary", 2000.0, 40000.0}    Sam := human.Human{"Sam", 10000.0, 500000.0}    Bank.Transfer(&Sam, &gary, 5000)}在上面的代码中可以正常使用Bank.Transfer(&Sam, &gary, 5000)但我认为应该是人类使用 Bank 函数,所以我如何将它重写成这个?Sam.Transfer(&gary, 5000)我试图在 Human.go 中导入 Bank.go,但出现了导入周期不允许的错误。我不确定是我的逻辑错误还是我糟糕的代码设计,但让我们看看是否有人可以解决这个问题。
查看完整描述

1 回答

?
DIEA

TA贡献1820条经验 获得超2个赞

这似乎是一个关于如何设计项目中的数据结构和操作及其依赖关系的一般软件工程问题。

正如您所发现的,循环导入是不好的。有很多方法可以改变设计来解耦事物。一个是清晰的层——例如,Bank应该依赖Human而不是相反。但是,如果您想提供方便的功能来将钱从 转移HumanHuman,您可以做的一件事是定义一个Bank对象将实现的接口。

然而,为简单起见,我建议严格分层。没有真正的理由 aHuman应该依赖于 a Bank。在极限情况下,这可能会变得太麻烦,因为Humans 需要更多服务(你会依赖HumanaBus来让Buses 移动Humans 吗?)


为了回答评论和更新的问题,我会保持简单:

package main


import (

    "fmt"

    "log"

)


type Human struct {

    ID   int64

    Name string

}


type Account struct {

    ID int64


    // Note: floats aren't great for representing money as they can lose precision

    // in some cases. Keeping this for consistency with original.

    Cash float64


    DaysSinceActive int64

}


type Bank struct {

    Accounts map[int64]Account

}


// Not checking negatives, etc. Don't use this for real banking :-)

func (bank *Bank) Transfer(src int64, dest int64, sum float64) error {

    srcAcct, ok := bank.Accounts[src]

    if !ok {

        return fmt.Errorf("source account %d not found", src)

    }

    destAcct, ok := bank.Accounts[dest]

    if !ok {

        return fmt.Errorf("destination account %d not found", dest)

    }

    // bank.Accounts[src] fetches a copy of the struct, so we have to assign it

    // back after modifying it.

    srcAcct.Cash -= sum

    bank.Accounts[src] = srcAcct

    destAcct.Cash += sum

    bank.Accounts[dest] = destAcct

    return nil

}


func main() {

    gary := Human{19928, "Gary"}

    sam := Human{99555, "Sam"}


    bank := Bank{Accounts: map[int64]Account{}}

    bank.Accounts[gary.ID] = Account{gary.ID, 250.0, 10}

    bank.Accounts[sam.ID] = Account{sam.ID, 175.0, 5}


    fmt.Println("before transfer", bank)


    if err := bank.Transfer(gary.ID, sam.ID, 25.0); err != nil {

        log.Fatal(err)

    }


    fmt.Println("after transfer", bank)

}

这段代码使用了松散耦合。银行需要知道的关于一个人的所有信息就是他们的 ID(可以是 SSN 或根据姓名、出生日期和其他内容计算出来的东西)来唯一地识别他们。人类应该持有银行(如果一个人在多家银行都有账户怎么办?)。银行不应该持有人(如果账户属于多个人、公司、虚拟实体怎么办?)等等。这里不需要接口,如果确实需要,您可以安全地将每种数据类型放在它们自己的包中。


查看完整回答
反对 回复 2023-06-01
  • 1 回答
  • 0 关注
  • 124 浏览
慕课专栏
更多

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信