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

时间:2019-01-17 09:45:58

标签: go code-design case-study

我是Golang的新手,我举了一个学习它的示例,但是我面临的示例不允许导入周期,所以有人知道如何避免这种情况吗?这是我的代码。

去银行

package Bank

import (
    "../../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")
    }
}

Human.go

package human

// import "../../func/Bank"

type Human struct {
    Name string
    Cash float64
    Bank float64
}

func (h *Human) Transfer(Receiver Human, payment float64) {

}

Main.go

package main

import (
    "./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,但出现了导入周期不允许错误。 我不确定这是我的逻辑错误还是我的错误代码设计,但请问是否有人可以解决此问题。

下面的更新内容

在阅读了消息之后,我仍然不了解如何在这种情况下实现Interface。但是,我确实更改了代码,请看一看它在golang的代码设计中是否更好,还是仍然相同?谢谢。

package main

// Human.go
type Human struct {
    Name string
    Cash float64
    Bank float64
}

// Bank.go
type Bank struct {
    Acct *Human
}

func (b *Bank) Transfer(receiver *Human, payment float64) {
    payer := b.Acct
    payer.Bank -= payment
    receiver.Bank += payment
}

// main.go
func main() {
    gary := Human{"Gary", 2000.0, 40000.0}
    Sam := Human{"Sam", 10000.0, 500000.0}

    Sam_Account := Bank{&Sam}

    Sam_Account.Transfer(&gary, 5000)

}

1 个答案:

答案 0 :(得分:0)

欢迎使用Golang和堆栈溢出!

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

您已经发现,循环导入是不好的。有许多方法可以更改设计以使事物脱钩。一个是清晰的图层-例如,Bank应该可能依赖于Human,而不是相反。但是,如果您想提供方便的功能来将钱从Human转移到Human,您可以做的一件事就是定义一个Bank对象将实现的接口。

为简单起见,我建议严格分层。没有Human应该依赖Bank的真正原因。由于Human需要更多的服务(在这种情况下,这样做可能会非常麻烦)(您是否希望Human依赖Bus才能使Bus es移动Human?)


要回答评论和更新的问题,我将使其保持简单:

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或根据姓名,出生日期和其他事物计算得出的某个数字)即可唯一地识别他们。人类应该持有银行(如果一个人在多个银行都有账户,该怎么办?)。银行不应该拥有人员(如果帐户属于多个人,公司,虚拟实体该怎么办?)等等。这里不需要接口,如果确实需要,您可以安全地将每种数据类型放在自己的包中。