撰寫銀行核心套件

已完成

現在我們已經讓基礎專案與測試檔案一起執行,接下來讓我們開始撰寫程式碼,實作前一個單元的功能和需求。 我們在這裡回顧了稍早討論的幾個主題,例如錯誤、結構和方法。

開啟 $GOPATH/src/bankcore/bank.go 檔案,接著移除 Hello() 函式,然後開始撰寫我們的線上銀行系統核心邏輯。

建立客戶和帳戶的結構

首先要建立一個 Customer 結構,收集想成為銀行客戶人士的姓名、地址和電話號碼。 此外,我們還需要 Account 資料的結構。 因為一名客戶可以有多個帳戶,所以我們會將客戶資訊內嵌到帳戶物件中。 其實就是要建立在 TestAccount 測試中定義的內容。

我們需要的結構可能像以下程式碼範例:

package bank

// Customer ...
type Customer struct {
    Name    string
    Address string
    Phone   string
}

// Account ...
type Account struct {
    Customer
    Number  int32
    Balance float64
}

現在,當您在終端機中執行 go test -v 命令時,您應該會看到測試已通過:

=== RUN   TestAccount
--- PASS: TestAccount (0.00s)
PASS
ok      github.com/msft/bank    0.094s

這項測試之所以會通過,是因為我們實作了 CustomerAccount 的結構。 有了結構後,讓我們撰寫方法,在銀行初始版本中新增所需的功能。 這些功能包括存款、提款和轉帳。

實作存款方法

我們需要從一個允許在帳戶中增加金額的方法開始。 但在這麼做之前,要先在 bank_test.go 檔案中建立 TestDeposit 函式:

func TestDeposit(t *testing.T) {
    account := Account{
        Customer: Customer{
            Name:    "John",
            Address: "Los Angeles, California",
            Phone:   "(213) 555 0147",
        },
        Number:  1001,
        Balance: 0,
    }

    account.Deposit(10)

    if account.Balance != 10 {
        t.Error("balance is not being updated after a deposit")
    }
}

當您執行 go test -v 時,您應該會在輸出中看到失敗的測試:

# github.com/msft/bank [github.com/msft/bank.test]
./bank_test.go:32:9: account.Deposit undefined (type Account has no field or method Deposit)
FAIL    github.com/msft/bank [build failed]

為滿足前一項測試,讓我們建立 Account 結構的 Deposit 方法:如果收到的金額等於或小於零,則會傳回錯誤; 否則,只要將收到的金額加入帳戶餘額即可。

請為 Deposit 方法使用下列程式碼:

// Deposit ...
func (a *Account) Deposit(amount float64) error {
    if amount <= 0 {
        return errors.New("the amount to deposit should be greater than zero")
    }

    a.Balance += amount
    return nil
}

當您執行 go test -v 時,應該會看到測試已通過:

=== RUN   TestAccount
--- PASS: TestAccount (0.00s)
=== RUN   TestDeposit
--- PASS: TestDeposit (0.00s)
PASS
ok      github.com/msft/bank    0.193s

您也可以撰寫測試,確認嘗試存入負數金額時會收到錯誤,如下所示:

func TestDepositInvalid(t *testing.T) {
    account := Account{
        Customer: Customer{
            Name:    "John",
            Address: "Los Angeles, California",
            Phone:   "(213) 555 0147",
        },
        Number:  1001,
        Balance: 0,
    }

    if err := account.Deposit(-10); err == nil {
        t.Error("only positive numbers should be allowed to deposit")
    }
}

當您執行 go test -v 命令時,應該會看到測試已通過:

=== RUN   TestAccount
--- PASS: TestAccount (0.00s)
=== RUN   TestDeposit
--- PASS: TestDeposit (0.00s)
=== RUN   TestDepositInvalid
--- PASS: TestDepositInvalid (0.00s)
PASS
ok      github.com/msft/bank    0.197s

注意

從這裡開始,我們會為每個方法撰寫一個測試案例。 但是您應該為程式撰寫盡可能多的測試,以便應對各種預期和非預期的情況。 例如,此案例會測試錯誤處理邏輯。

實作提款方法

撰寫 Withdraw 功能之前,要先撰寫其測試:

func TestWithdraw(t *testing.T) {
    account := Account{
        Customer: Customer{
            Name:    "John",
            Address: "Los Angeles, California",
            Phone:   "(213) 555 0147",
        },
        Number:  1001,
        Balance: 0,
    }

    account.Deposit(10)
    account.Withdraw(10)

    if account.Balance != 0 {
        t.Error("balance is not being updated after withdraw")
    }
}

當您執行 go test -v 命令時,您應該會在輸出中看到失敗的測試:

# github.com/msft/bank [github.com/msft/bank.test]
./bank_test.go:67:9: account.Withdraw undefined (type Account has no field or method Withdraw)
FAIL    github.com/msft/bank [build failed]

讓我們實作 Withdraw 方法的邏輯,收到的參數金額即為帳戶餘額減少的金額。 與前面的流程一樣,我們必須驗證收到的數字是否大於零,以及帳戶餘額是否足夠。

請為 Withdraw 方法使用下列程式碼:

// Withdraw ...
func (a *Account) Withdraw(amount float64) error {
    if amount <= 0 {
        return errors.New("the amount to withdraw should be greater than zero")
    }

    if a.Balance < amount {
        return errors.New("the amount to withdraw should be less than the account's balance")
    }

    a.Balance -= amount
    return nil
}

當您執行 go test -v 命令時,應該會看到測試已通過:

=== RUN   TestAccount
--- PASS: TestAccount (0.00s)
=== RUN   TestDeposit
--- PASS: TestDeposit (0.00s)
=== RUN   TestDepositInvalid
--- PASS: TestDepositInvalid (0.00s)
=== RUN   TestWithdraw
--- PASS: TestWithdraw (0.00s)
PASS
ok      github.com/msft/bank    0.250s

實作對帳單方法

讓我們撰寫一道方法,列印包含帳戶名稱、數字和餘額的對帳單。 但首先要建立 TestStatement 函式:

func TestStatement(t *testing.T) {
    account := Account{
        Customer: Customer{
            Name:    "John",
            Address: "Los Angeles, California",
            Phone:   "(213) 555 0147",
        },
        Number:  1001,
        Balance: 0,
    }

    account.Deposit(100)
    statement := account.Statement()
    if statement != "1001 - John - 100" {
        t.Error("statement doesn't have the proper format")
    }
}

當您執行 go test -v 時,您應該會在輸出中看到失敗的測試:

# github.com/msft/bank [github.com/msft/bank.test]
./bank_test.go:86:22: account.Statement undefined (type Account has no field or method Statement)
FAIL    github.com/msft/bank [build failed]

讓我們撰寫 Statement 方法,其應會傳回字串。 (您稍後的挑戰為覆寫此方法)使用下列程式碼:

// Statement ...
func (a *Account) Statement() string {
    return fmt.Sprintf("%v - %v - %v", a.Number, a.Name, a.Balance)
}

當您執行 go test -v 時,應該會看到測試已通過:

=== RUN   TestAccount
--- PASS: TestAccount (0.00s)
=== RUN   TestDeposit
--- PASS: TestDeposit (0.00s)
=== RUN   TestDepositInvalid
--- PASS: TestDepositInvalid (0.00s)
=== RUN   TestWithdraw
--- PASS: TestWithdraw (0.00s)
=== RUN   TestStatement
--- PASS: TestStatement (0.00s)
PASS
ok      github.com/msft/bank    0.328s

讓我們繼續下一節的內容,撰寫 Web API 公開 Statement 方法。