銀行元帳
CoddyのKotlinジャーニー「オブジェクト指向プログラミング」セクションの一部。レッスン 57/57。
チャレンジ
難しい小さな銀行を作成してください。
BankError(message)はExceptionを拡張するシールドクラスで、サブクラスとしてInvalidAmount(invalid amount)、Insufficient(id)(insufficient funds in 2)、WithdrawalLimit(id)(withdrawal limit on 2)、NoAccount(id)(no account 9)があります。Account(id, owner)は抽象クラスです。そのbalanceはどこからでも読み取れますが、変更できるのはアカウントクラスだけです。deposit(amount)とwithdraw(amount)は、1未満の金額に対してInvalidAmountをスローし、withdrawは残高がアカウントのlowestBalanceを下回る場合にInsufficientをスローします。抽象kindと、何もしないオープンなmonthEnd()を宣言します。Checkingでは、-100までの当座貸越が可能です。Savingsの残高は0未満にならず、月あたり最大2回まで引き出せます(WithdrawalLimit)。また、monthEnd()では1%の利息(切り捨て)を加算し、引き出し回数をリセットします。Bankには、open(kind, id, owner)、account(id)(NoAccountをスロー)、最初に両方のアカウントを検索してから引き出しと預け入れを行うtransfer(from, to, amount)、すべてのアカウントに対するmonthEnd()、およびid順にソートされたaccounts()があります。
用意されているコードは、open checking 1 ada、open savings 2 bo、dep 1 100、wd 2 30、tr 1 2 50またはmonthのコマンドを読み取り、okまたはエラーのメッセージを出力し、最後に各アカウントを1 ada checking: -20の形式で出力します。
コードはBankError.kt、InvalidAmount.kt、Insufficient.kt、WithdrawalLimit.kt、NoAccount.kt、Account.kt、Checking.kt、Savings.kt、Bank.ktに記述します。Main.ktには用意された入出力コードが含まれており、編集できません。
自分で試してみよう
fun main() {
// 提供された入出力コード: そのままにしておく
val input = generateSequence(::readLine).toList()
val bank = Bank()
for (cmd in input) {
val p = cmd.split(" ")
try {
when (p[0]) {
"open" -> bank.open(p[1], p[2].toInt(), p[3])
"dep" -> bank.account(p[1].toInt()).deposit(p[2].toInt())
"wd" -> bank.account(p[1].toInt()).withdraw(p[2].toInt())
"tr" -> bank.transfer(p[1].toInt(), p[2].toInt(), p[3].toInt())
else -> bank.monthEnd()
}
println("ok")
} catch (e: BankError) {
println(e.message)
}
}
for (a in bank.accounts()) println("${a.id} ${a.owner} ${a.kind}: ${a.balance}")
}
オブジェクト指向プログラミングのすべてのレッスン
自分で練習してみよう: Kotlinプレイグラウンド