銀行システム
CoddyのPHPジャーニー「オブジェクト指向プログラミング」セクションの一部 — レッスン 89/91。
チャレンジ
簡単口座、取引、および口座間の送金を処理する銀行システムを構築しましょう。財務データを保護するための適切なカプセル化、異なる口座タイプのための継承、およびデータの整合性を確保するためのバリデーションを備えたシステムを作成します。
コードは以下の6つのファイルに分けて構成します:
Transaction.php— 財務操作を記録するためのTransactionクラスを作成します。type(string — "deposit", "withdrawal", または "transfer")、amount(float)、およびtimestamp(string) にはコンストラクタのプロパティ昇格(constructor promotion)を使用してください。すべてのプロパティは public readonly にする必要があります。"[type]: $[amount]"を返すgetDescription(): stringメソッドを追加してください(金額は小数点以下2桁にフォーマットします)。BankAccount.php— Transaction ファイルをインクルードします。以下の内容を持つ抽象クラスBankAccountを作成します:accountNumber(string, public readonly) とholderName(string, public readonly) のコンストラクタプロパティ昇格- 0.0 で初期化された protected な
$balanceプロパティ - 取引履歴を保存するための private な配列
getBalance(): float— 現在の残高を返しますdeposit(float $amount): string— 残高を加算し、取引を記録し、"Deposited: $[amount]"を返しますabstract withdraw(float $amount): string— 子クラスで引き出しルールを実装しますaddTransaction(Transaction $t): void— 履歴に取引を追加するための protected メソッドgetTransactionCount(): int— 取引件数を返します
SavingsAccount.php— BankAccount ファイルをインクルードします。BankAccountを継承したSavingsAccountクラスを作成します:- クラス定数
INTEREST_RATEを 0.02 (2%) に設定します withdraw(float $amount): string— 金額が残高を超える場合は"Insufficient funds"を返し、そうでない場合は差し引き、取引を記録し、"Withdrew: $[amount]"を返しますapplyInterest(): string— 現在の残高に対する利息を計算し、預金として追加し、"Interest applied: $[interest]"を返します
- クラス定数
CheckingAccount.php— BankAccount ファイルをインクルードします。BankAccountを継承したCheckingAccountクラスを作成します:- コンストラクタ経由で設定される private な
$overdraftLimitプロパティ(デフォルト値 100.0 の第3引数を追加します) withdraw(float $amount): string— 金額が「残高 + 当座貸越限度額」を超えない場合に引き出しを許可します。失敗した場合は"Insufficient funds (overdraft limit: $[limit])"を、成功した場合は"Withdrew: $[amount]"を返しますgetOverdraftLimit(): float— 当座貸越限度額を返します
- コンストラクタ経由で設定される private な
Bank.php— 両方の口座ファイルをインクルードします。口座を管理するBankクラスを作成します:- 口座番号をインデックスとした口座を保存する private な配列
addAccount(BankAccount $account): void— 口座を追加しますfindAccount(string $accountNumber): ?BankAccount— 口座または null を返しますtransfer(string $fromAccount, string $toAccount, float $amount): string— 口座間で送金を行います。"Source account not found"、"Destination account not found"、または引き出しが失敗した場合はその結果を返します。成功した場合は、宛先に入金し、"Transferred $[amount] from [from] to [to]"を返します
main.php— Bank ファイルをインクルードします。口座データ (JSON) と操作 (JSON) の2つの入力を受け取ります。口座 JSON フォーマット:
[{"type": "savings", "number": "SAV001", "holder": "Alice", "initial": 1000}, {"type": "checking", "number": "CHK001", "holder": "Bob", "initial": 500, "overdraft": 200}]操作 JSON フォーマット:
[{"op": "deposit", "account": "SAV001", "amount": 200}, {"op": "withdraw", "account": "CHK001", "amount": 600}, {"op": "transfer", "from": "SAV001", "to": "CHK001", "amount": 300}, {"op": "interest", "account": "SAV001"}, {"op": "balance", "account": "SAV001"}]銀行とすべての口座を作成します(初期金額を入金します)。各操作を処理します:
"deposit"—deposit()の結果を出力します"withdraw"—withdraw()の結果を出力します"transfer"—transfer()の結果を出力します"interest"—applyInterest()の結果を出力します(普通預金口座のみ)"balance"—"[holder] balance: $[balance]"を出力します(小数点以下2桁)
各結果を新しい行に出力してください。すべての金額は小数点以下2桁にフォーマットしてください。
送金はアトミックである必要があることに注意してください。つまり、引き出しが失敗した場合、入金は行われません。protected な $balance プロパティにより、外部のコードからは隠蔽しつつ、子クラスがそれを変更できるようになっており、金融の文脈における適切なカプセル化を示しています。
自分で試してみよう
<?php
require_once 'Bank.php';
// 入力を読み込む
$accountsJson = trim(fgets(STDIN));
$operationsJson = trim(fgets(STDIN));
// JSON入力をパースする
$accounts = (array)json_decode($accountsJson, true);
$operations = (array)json_decode($operationsJson, true);
// TODO: Bankのインスタンスを作成する
// TODO: アカウントデータからアカウントを作成する
// $accounts内の各アカウントについて:
// - typeが "savings" の場合、SavingsAccountを作成
// - typeが "checking" の場合、CheckingAccountを作成(当座貸越が提供されている場合はそれを含める)
// - 銀行にアカウントを追加
// - 初回金額を入金
// TODO: 各操作を処理する
// $operations内の各操作について:
// - "deposit": deposit() の結果を出力
// - "withdraw": withdraw() の結果を出力
// - "transfer": transfer() の結果を出力
// - "interest": applyInterest() の結果を出力(普通預金のみ)
// - "balance": "[holder] balance: $[balance]" を出力(小数点以下2桁)
?>