Разработка программ Go, чтобы избежать циклических зависимостей - PullRequest
0 голосов
/ 17 января 2019

Я новичок в Голанге, и я делаю пример для изучения этого, но я сталкиваюсь с циклом импорта, не разрешенным в моем примере, так что кто-нибудь знает, как этого избежать? Вот мой код.

Bank, идти

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)

Но я думаю, что Человек должен использовать функцию Банка, так как я могу переписать это в этом?

Sam.Transfer(&gary, 5000)

Я попытался импортировать Bank.go в Human.go, но ошибка цикла импорта не разрешена. Я не уверен, что это моя логическая ошибка или мой плохой дизайн кода, но давайте посмотрим, может ли кто-то иметь решение по этому вопросу.

Обновленный контент ниже

После прочтения сообщения я все еще не понимаю, как реализовать интерфейс в этом сценарии. Однако я изменил свой код, пожалуйста, посмотрите, насколько он лучше в дизайне кода для 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 голосов
/ 17 января 2019

Добро пожаловать на Голанг и переполнение стека!

Похоже, это общий вопрос разработки программного обеспечения о том, как проектировать структуры данных и операции в вашем проекте и их зависимости.

Как вы обнаружили, круговой импорт плох. Есть много способов изменить дизайн, чтобы отделить вещи. Один из них - прозрачные слои - например, Bank, вероятно, должен зависеть от Human, но не наоборот. Однако, если вы хотите предоставить удобную функциональность для перевода денег с Human на Human, вы могли бы определить интерфейс, который будет реализован объектом Bank.

Однако для простоты я бы порекомендовал строгое наложение. Нет реальной причины, по которой Human должен зависеть от Bank. В пределе это может оказаться слишком громоздким, поскольку Human s нужно больше услуг (вы бы поставили зависимость Human от Bus, чтобы Bus могли перемещать 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)
}

Этот код использует слабую связь, как упоминалось в моем первоначальном ответе. Все, что банку нужно знать о человеке, - это его удостоверение личности (может быть SSN или что-то, вычисленное по имени, дате рождения и другим вещам), чтобы идентифицировать их однозначно. Люди должны держать банки (что, если у человека есть счета в нескольких банках?). Банки не должны содержать людей (что делать, если счета принадлежат нескольким лицам, корпорациям, виртуальным объектам?) И так далее. Здесь нет необходимости в интерфейсах, и вы можете безопасно поместить каждый тип данных в свой собственный пакет, если вам действительно нужно.

...