Menu
Coddy logo textTech

銀行システム

CoddyのGOジャーニー「オブジェクト指向プログラミング」セクションの一部。レッスン 106/107。

challenge icon

チャレンジ

簡単

口座、取引、金融操作を管理する Banking System を構築しましょう!データの整合性が重要となる堅牢なシステムを作成します。口座残高は検証済みのメソッドを通じてのみ変更され、残高不足や無効な金額などのエラーは適切に処理される必要があります。

コードを 5 つのファイルに分けて整理します。

  • account.go: Deposit(amount float64) error メソッドと Withdraw(amount float64) error メソッドを持つ Transactable インターフェースを Define します。次に、このインターフェースを実装する 2 種類の口座を作成します。

    非エクスポートの idholderbalance フィールドを持つ CheckingAccount。CheckingAccount では、残高を正確にゼロにする引き出しは Allow しますが、ゼロを下回ることは許可しません。

    同じ非エクスポートフィールドに加えて minBalance フィールドを持つ SavingsAccount。SavingsAccount は 100.0 の最低残高を維持する必要があり、このしきい値を下回る引き出しは失敗させます。

    両方の口座タイプにはコンストラクター(NewCheckingAccountNewSavingsAccount)および Balance() float64 getter メソッドが必要です。入金では、負の金額またはゼロの金額を拒否します。引き出しでは、負の金額、ゼロの金額、および available な資金を超える金額を拒否します。

  • transaction.go: 口座の activity を記録する Transaction 構造体を Create します。各取引には、Type(string: "deposit" または "withdrawal")、Amount(float64)、AccountID(string)が含まれます。NewTransaction コンストラクターと、金額を小数点以下 2 桁でフォーマットして [Type] $[Amount] on [AccountID] 形式を返す String() string メソッドを追加します。
  • bank.go: 複数の口座を管理し、取引を記録する Bank 構造体を構築します。口座は ID をキーとする map に格納し、取引の slice も保持します。次を実装します。
    • NewBank() *Bank: empty な collections を初期化します
    • AddAccount(id string, account Transactable): 口座を登録します
    • Deposit(accountID string, amount float64) error: 口座に入金し、成功時に取引を記録します
    • Withdraw(accountID string, amount float64) error: 口座から引き出し、成功時に取引を記録します
    • Transfer(fromID, toID string, amount float64) error: 口座間で atomically に資金を移動します(引き出しが成功して入金が失敗した場合は、引き出しを元に戻します)
    • GetBalance(accountID string) (float64, error): 口座の残高を返します

    存在しない口座に対しては appropriate なエラーを返します。

  • errors.go: banking 操作の sentinel エラーを Define します。
    • ErrAccountNotFound: 口座 ID が存在しない場合
    • ErrInsufficientFunds: 引き出し金額が available な残高を超える場合
    • ErrInvalidAmount: 入金額または引き出し額がゼロまたは負の場合
    • ErrMinBalanceRequired: SavingsAccount の引き出しが最低残高に違反する場合
  • main.go: Bank を Create し、入力に基づいて操作を処理します。

    Create する口座数を読み取ります。各口座について、口座タイプ(checking または savings)、ID、holder 名、初期残高を読み取ります。appropriate な口座タイプを作成し、Bank に追加します。

    次に操作数を読み取ります。各操作は次のいずれかです。

    • deposit [accountID] [amount]
    • withdraw [accountID] [amount]
    • transfer [fromID] [toID] [amount]
    • balance [accountID]

    各操作について結果を出力します。入金、引き出し、Transfer が成功した場合は OK、balance クエリの場合は小数点以下 2 桁でフォーマットした残高、失敗した場合は sentinel エラーメッセージを使用した Error: [message] を出力します。

次の入力が提供されます。

  • 口座数、その後に各口座の type、ID、holder、初期残高(各 4 行)
  • 操作数、その後に各操作(各 1 行)

例えば、次の入力が与えられた場合:

2
checking
ACC001
Alice
500.00
savings
ACC002
Bob
1000.00
6
deposit ACC001 200
balance ACC001
withdraw ACC002 950
withdraw ACC001 800
transfer ACC001 ACC002 100
balance ACC002

出力は次のようになります。

OK
700.00
Error: minimum balance required
Error: insufficient funds
OK
1000.00

また、次の入力が与えられた場合:

1
checking
C100
Charlie
100.00
4
withdraw C100 100
balance C100
deposit C100 -50
withdraw C999 50

出力は次のようになります。

OK
0.00
Error: invalid amount
Error: account not found

Transactable インターフェースによって Bank は両方の口座タイプを統一的に扱うことができ、一方で各口座タイプは独自のルールを適用している点に注目してください。Transfer メソッドは atomic な操作を示しています。途中で問題が発生しても、システムは一貫性を保ちます。

自分で試してみよう

package main

import (
	"bufio"
	"fmt"
	"os"
	"strconv"
	"strings"
)

func main() {
	scanner := bufio.NewScanner(os.Stdin)

	// 口座数を読み取る
	scanner.Scan()
	numAccounts, _ := strconv.Atoi(scanner.Text())

	// 新しい銀行を作成する
	bank := NewBank()

	// 口座を読み取り作成する
	for i := 0; i < numAccounts; i++ {
		scanner.Scan()
		accountType := scanner.Text()
		scanner.Scan()
		id := scanner.Text()
		scanner.Scan()
		holder := scanner.Text()
		scanner.Scan()
		balance, _ := strconv.ParseFloat(scanner.Text(), 64)

		// TODO: accountTypeに基づいて適切な口座タイプを作成する
		// そしてそれを銀行に追加する
		_ = accountType
		_ = id
		_ = holder
		_ = balance
	}

	// 操作数を読み取る
	scanner.Scan()
	numOps, _ := strconv.Atoi(scanner.Text())

	// 各操作を処理する
	for i := 0; i < numOps; i++ {
		scanner.Scan()
		parts := strings.Fields(scanner.Text())
		operation := parts[0]

		// TODO: 各操作タイプを処理する:
		// - "deposit": 口座に入金し、"OK"またはエラーを出力する
		// - "withdraw": 口座から出金し、"OK"またはエラーを出力する
		// - "transfer": 口座間で送金し、"OK"またはエラーを出力する
		// - "balance": 残高を取得し、小数点以下2桁でフォーマットして出力する
		_ = operation
	}
}

オブジェクト指向プログラミングのすべてのレッスン

自分で練習してみよう: Goオンラインコンパイラ