Menu
Coddy logo textTech

銀行システム

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

challenge icon

チャレンジ

簡単

State パターン、継承、およびスマートポインタ管理を実証する銀行システムを構築しましょう。アカウントが異なる状態(Active、Frozen、Closed)を持ち、それぞれの状態でトランザクションの処理動作が異なるシステムを作成します。

コードは以下の 6 つのファイルに分けて構成します:

  • AccountState.h: アカウントの状態に関する State パターンのインフラストラクチャを定義します。

    純粋仮想メソッドを持つ抽象クラス AccountState 基底クラスを作成します:

    • deposit(double amount) — 入金が許可される場合は true、そうでない場合は false を返します
    • withdraw(double amount) — 出金が許可される場合は true、そうでない場合は false を返します
    • getStateName() — 状態名を文字列として返します

    3 つの具体的な状態を実装します:

    • ActiveState: 入金と出金の両方を許可し、"Active" を返します
    • FrozenState: 入金は許可しますが、出金は拒否し、"Frozen" を返します
    • ClosedState: 入金と出金の両方を拒否し、"Closed" を返します
  • Account.h: 状態管理を備えたアカウントの階層を構築します。

    以下の内容を持つ基底クラス Account を作成します:

    • 口座番号(文字列)、所有者名、および残高
    • 現在の状態を管理するための std::unique_ptr<AccountState>
    • deposit(double amount) — まず状態を確認し、許可された場合は残高に追加して "Deposited [amount] to [accountNumber]" と出力します。許可されない場合は "Deposit rejected: account is [stateName]" と出力します。
    • withdraw(double amount) — 状態を確認し、許可され、かつ十分な残高がある場合は、残高から差し引いて "Withdrew [amount] from [accountNumber]" と出力します。状態が拒否した場合は "Withdrawal rejected: account is [stateName]" と出力します。残高不足の場合は "Insufficient funds" と出力します。
    • freeze()activate()close() — アカウントの状態を変更します
    • getInfo()"[accountNumber] ([ownerName]): $[balance] - [stateName]" を返します
    • 仮想関数 getAccountType()"Basic" を返します

    2 つの派生アカウントタイプを作成します:

    • SavingsAccount: 最低残高要件(コンストラクタに渡される)を持ちます。withdraw をオーバーライドして、出金によって最低残高を下回らないかどうかも確認します。下回る場合は "Cannot go below minimum balance of [minimum]" と出力します。アカウントタイプとして "Savings" を返します。
    • CheckingAccount: 当座貸越限度額(コンストラクタに渡される)を持ちます。(残高 + overdraftLimit)までの出金を許可します。アカウントタイプとして "Checking" を返します。
  • Transaction.h: トランザクションをオブジェクトとしてモデル化します。

    以下の情報を記録する Transaction クラスを作成します:

    • トランザクションタイプ(文字列: "Deposit"、"Withdrawal"、"Transfer")
    • 金額
    • 関連する口座番号
    • 成功ステータス (bool)

    "[type]: $[amount] on [accountNumber] - [Success/Failed]" を返す getDescription() メソッドを追加します。

  • Customer.h: 複数のアカウントを所有する顧客を作成します。

    Customer は名前、ID、および std::shared_ptr<Account> のベクターを持ちます。以下を実装してください:

    • addAccount(std::shared_ptr<Account> account)
    • getAccount(const std::string& accountNumber) — 一致するアカウントまたは nullptr を返します
    • getTotalBalance() — すべてのアカウントの残高を合計します
    • listAccounts() — 各アカウントの情報を出力します
  • Bank.h: 銀行業務を調整します。

    Bank クラスは顧客を保存し、トランザクション履歴を維持します。以下を実装してください:

    • addCustomer(std::shared_ptr<Customer> customer)
    • findAccount(const std::string& accountNumber) — すべての顧客からそのアカウントを検索します
    • processDeposit(const std::string& accountNumber, double amount) — アカウントを見つけ、入金を実行し、トランザクションを記録します
    • processWithdrawal(const std::string& accountNumber, double amount) — アカウントを見つけ、出金を実行し、トランザクションを記録します
    • getTransactionCount() — 処理された総トランザクション数を返します
  • main.cpp: 銀行システムを実演します。

    4 つの入力を読み取ります:

    1. 顧客の詳細(形式: name,customerId
    2. 貯蓄口座の詳細(形式: accountNumber,minBalance
    3. 操作 1(形式: action,accountNumber,amount。action は deposit または withdraw
    4. 操作 2(形式: action,accountNumber,amount または freeze,accountNumber

    銀行と顧客を作成します。顧客を所有者とする貯蓄口座を作成し、それを顧客に追加します。顧客を銀行に追加します。両方の操作を処理します(freeze コマンドの場合は、アカウントを凍結してから、状態を実証するために $10 の少額の出金を試みます)。最後に、顧客のアカウントリストと総トランザクション数を "Transactions: [count]" として出力します。

例えば、入力が Alice,C001SAV001,100deposit,SAV001,500withdraw,SAV001,200 の場合:

Deposited 500 to SAV001
Withdrew 200 from SAV001
SAV001 (Alice): $300 - Active
Transactions: 2

入力が Bob,C002SAV002,50deposit,SAV002,100freeze,SAV002 の場合:

Deposited 100 to SAV002
Withdrawal rejected: account is Frozen
SAV002 (Bob): $100 - Frozen
Transactions: 2

入力が Carol,C003SAV003,200deposit,SAV003,250withdraw,SAV003,100 の場合:

Deposited 250 to SAV003
Cannot go below minimum balance of 200
SAV003 (Carol): $250 - Active
Transactions: 2

このチャレンジは、アカウントの動作を管理するための State パターン、異なるアカウントタイプのための継承、所有関係のためのスマートポインタ、およびトランザクションの追跡を統合したものであり、これらはすべてこのコースを通じて習得したコアコンセプトです。

自分で試してみよう

#include <iostream>
#include <string>
#include <sstream>
#include <memory>
#include "Bank.h"

int main() {
    // 入力1を読み込む: 顧客の詳細 (形式: name,customerId)
    std::string customerInput;
    std::getline(std::cin, customerInput);
    
    // 入力2を読み込む: 普通預金口座の詳細 (形式: accountNumber,minBalance)
    std::string accountInput;
    std::getline(std::cin, accountInput);
    
    // 入力3を読み込む: 操作1 (形式: action,accountNumber,amount)
    std::string op1Input;
    std::getline(std::cin, op1Input);
    
    // 入力4を読み込む: 操作2 (形式: action,accountNumber,amount または freeze,accountNumber)
    std::string op2Input;
    std::getline(std::cin, op2Input);
    
    // TODO: customerInput から顧客の詳細を解析する
    // ヒント: 文字列を分割するには find(',') を使用する
    
    // TODO: accountInput から口座の詳細を解析する
    
    // TODO: 銀行を作成する
    
    // TODO: 顧客を作成する
    
    // TODO: 顧客を所有者として普通預金口座を作成する
    
    // TODO: 顧客に口座を追加する
    
    // TODO: 銀行に顧客を追加する
    
    // TODO: 操作1を解析して処理する
    // 形式: action,accountNumber,amount (action は "deposit" または "withdraw")
    
    // TODO: 操作2を解析して処理する
    // 次のいずれか: action,accountNumber,amount または freeze,accountNumber
    // freeze の場合、口座を凍結してから $10 の引き出しを試みる
    
    // TODO: 顧客の口座リストを表示する
    
    // TODO: 取引件数を "Transactions: [count]" として表示する
    
    return 0;
}

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