Menu
Coddy logo textTech

銀行システム

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

このチャレンジでは、OOP(オブジェクト指向プログラミング)の原則を使用して、現実世界の金融業務をモデル化した銀行システムを構築することに挑戦します。

システムは、SavingsAccountCheckingAccount のような異なるアカウントタイプを処理する必要があります。これらは両方とも、ベースとなる Account クラスを継承します。各アカウントタイプには独自の動作があります。例えば、普通預金口座は利息が発生する場合があり、当座預金口座は当座貸越保護を持つ場合があります。

トランザクションをどのようにモデル化するか考えてみましょう。Transactable のようなインターフェースで、deposit()withdraw() といったメソッドを定義できます。無効な操作を適切に処理するために、InsufficientFundsException のようなカスタム例外の使用を検討してください。

Bankクラスは、複数の口座や顧客を管理し、コンポジション(合成)を実証する場合があります。異なる口座タイプを作成するためにFactoryパターンを適用したり、口座の変更を顧客に通知するためにObserverパターンを使用したりすることが可能です。

残高や口座番号などの機密データを保護するためにカプセル化を重視し、異なる口座タイプを統一的に処理するためにポリモーフィズムを使用します。

challenge icon

チャレンジ

簡単

OOPの原則をマスターしたことを証明する、完全な銀行システムを構築しましょう!異なる口座タイプ、取引処理、適切な例外管理を備えたシステムを、複数のファイルに分けて作成します。

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

  • Transactable.java: 金融操作のコントラクトを定義するインターフェースを作成します。deposit(double amount)withdraw(double amount) メソッドを宣言し、どちらも成功を示す boolean を返すようにします。
  • InsufficientFundsException.java: Exception を継承したカスタム例外を作成します。コンストラクタは試行された引き出し額と現在の残高を受け取り、Insufficient funds: attempted [amount], available [balance] という形式のメッセージを作成します。
  • Account.java: 任意の銀行口座を表す抽象基底クラスを作成します。accountNumber (String) と balance (double) のプライベートフィールド、両方を初期化するコンストラクタ、適切なゲッター、サブクラスが変更できるように balance の protected セッター、および String を返す抽象メソッド getAccountType() を含めます。"[accountNumber] ([accountType]): $[balance]" を返し、残高を小数点以下2桁にフォーマットする getDetails() メソッドを実装します。
  • SavingsAccount.java: Account を継承し Transactable を実装するクラスを作成します。interestRate (double) のプライベートフィールドを追加します。コンストラクタは accountNumber、初期残高、および interestRate を受け取ります。getAccountType() を実装して "Savings" を返すようにします。deposit() では、金額を残高に加え、true を返します。withdraw() では、金額が残高を超える場合は false を返し、それ以外の場合は差し引いて true を返します。残高に利率の割合を掛けて増やす applyInterest() メソッドを追加します。
  • CheckingAccount.java: Account を継承し Transactable を実装するクラスを作成します。overdraftLimit (double) のプライベートフィールドを追加します。コンストラクタは accountNumber、初期残高、および overdraftLimit を受け取ります。getAccountType() を実装して "Checking" を返すようにします。deposit() では、金額を加えて true を返します。withdraw() では、金額が残高と overdraftLimit の合計以下の場合は引き出しを許可し、この制限を超える場合は false を返します。
  • Bank.java: ArrayList を使用して口座を管理するクラスを作成します。以下のメソッドを含めます:
    • addAccount(Account account) - 銀行に口座を追加します
    • findAccount(String accountNumber) - 口座を返すか、見つからない場合は null を返します
    • transfer(String fromAccount, String toAccount, double amount) - 口座間で送金を行います。元の口座の残高が不足している場合は InsufficientFundsException をスローします。成功した場合は "Transferred $[amount] from [fromAccount] to [toAccount]" を返します
    • getTotalDeposits() - すべての口座残高の合計を返します
  • Main.java: 銀行システムを構築しましょう!カンマ区切りのコマンド文字列を受け取ります:
    • SAVINGS:accountNumber:balance:interestRate
    • CHECKING:accountNumber:balance:overdraftLimit
    • DEPOSIT:accountNumber:amount
    • WITHDRAW:accountNumber:amount
    • TRANSFER:fromAccount:toAccount:amount
    • INTEREST:accountNumber

    各コマンドを処理し、以下を出力します:

    • SAVINGS/CHECKING: Account created: [accountNumber]
    • DEPOSIT: Deposited $[amount] to [accountNumber]
    • WITHDRAW: 成功時は Withdrew $[amount] from [accountNumber]、失敗時は Withdrawal failed: [accountNumber]
    • TRANSFER: 成功メッセージ、または Transfer failed: [exception message] を出力します
    • INTEREST: Interest applied to [accountNumber]

    すべてのコマンドの後、--- Bank Summary --- を出力し、続いて各口座の詳細(getDetails() を使用)を出力し、最後に小数点以下2桁にフォーマットされた Total deposits: $[total] を出力します。

入力として、カンマ区切りのコマンド文字列を1つ受け取ります。

例えば、入力が SAVINGS:SAV001:1000.00:0.05,CHECKING:CHK001:500.00:200.00,DEPOSIT:SAV001:250.00,WITHDRAW:CHK001:600.00,TRANSFER:SAV001:CHK001:100.00,INTEREST:SAV001 の場合、出力は以下のようになります:

Account created: SAV001
Account created: CHK001
Deposited $250.00 to SAV001
Withdrew $600.00 from CHK001
Transferred $100.00 from SAV001 to CHK001
Interest applied to SAV001
--- Bank Summary ---
SAV001 (Savings): $1207.50
CHK001 (Checking): $0.00
Total deposits: $1207.50

当座預金口座(Checking account)が、残高が $500 しかないにもかかわらず $600 の引き出しを許可していることに注目してください。これがオーバードラフト保護(当座貸越保護)の働きです!普通預金口座(Savings account)は、送金後の残高に対して 5% の利息が発生しています。このチャレンジは、継承、インターフェース、ポリモーフィズム、カスタム例外、およびカプセル化を現実的な銀行アプリケーションに統合したものです!

チートシート

銀行システムは、口座管理、取引、および例外処理を通じてOOP(オブジェクト指向プログラミング)の原則を実証します。

契約のためのインターフェース

インターフェースを使用して共通の操作を定義します:

public interface Transactable {
    boolean deposit(double amount);
    boolean withdraw(double amount);
}

カスタム例外

ドメイン固有のエラーに対してカスタム例外を作成します:

public class InsufficientFundsException extends Exception {
    public InsufficientFundsException(double attempted, double available) {
        super("Insufficient funds: attempted " + attempted + ", available " + available);
    }
}

抽象基底クラス

抽象クラスを使用して共通の構造を定義し、サブクラス固有の動作のために抽象メソッドを使用します:

public abstract class Account {
    private String accountNumber;
    private double balance;
    
    public abstract String getAccountType();
    
    protected void setBalance(double balance) {
        this.balance = balance;
    }
}

継承とポリモーフィズム

サブクラスは基底クラスを拡張し、インターフェースを実装します:

public class SavingsAccount extends Account implements Transactable {
    private double interestRate;
    
    @Override
    public String getAccountType() {
        return "Savings";
    }
    
    public void applyInterest() {
        setBalance(getBalance() * (1 + interestRate));
    }
}

コンポジション(合成)

クラスは他のオブジェクトのコレクションを管理できます:

public class Bank {
    private ArrayList<Account> accounts;
    
    public void addAccount(Account account) {
        accounts.add(account);
    }
    
    public Account findAccount(String accountNumber) {
        // 口座を検索して返すか、nullを返します
    }
}

カプセル化

privateフィールドを使用して機密データを保護し、getter/setterを通じてアクセスを制御します。カプセル化を維持しながらサブクラスからのアクセスを許可するには、protected修飾子を使用します。

取引における例外処理

無効な操作に対してカスタム例外をスローします:

public String transfer(String from, String to, double amount) throws InsufficientFundsException {
    if (!fromAccount.withdraw(amount)) {
        throw new InsufficientFundsException(amount, fromAccount.getBalance());
    }
    toAccount.deposit(amount);
    return "Transferred $" + amount;
}

自分で試してみよう

import java.util.Scanner;

class Main {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        String input = scanner.nextLine();
        
        // TODO: Bank インスタンスを作成する
        
        // TODO: 入力をカンマで分割して個別のコマンドを取得する
        
        // TODO: 各コマンドを処理する:
        // - SAVINGS:accountNumber:balance:interestRate -> SavingsAccount を作成し、"Account created: [accountNumber]" と出力する
        // - CHECKING:accountNumber:balance:overdraftLimit -> CheckingAccount を作成し、"Account created: [accountNumber]" と出力する
        // - DEPOSIT:accountNumber:amount -> 入金し、"Deposited $[amount] to [accountNumber]" と出力する
        // - WITHDRAW:accountNumber:amount -> 出金し、成功メッセージまたは "Withdrawal failed: [accountNumber]" と出力する
        // - TRANSFER:fromAccount:toAccount:amount -> 振込し、成功メッセージまたは "Transfer failed: [exception message]" と出力する
        // - INTEREST:accountNumber -> 利息を適用し、"Interest applied to [accountNumber]" と出力する
        
        // TODO: すべてのコマンドの後、"--- Bank Summary ---" と出力する
        
        // TODO: getDetails() を使用して各口座の詳細を出力する
        
        // TODO: 小数点以下2桁にフォーマットされた "Total deposits: $[total]" を出力する
    }
}
quiz icon腕試し

このレッスンには短いクイズがあります。レッスンを始めて解答し、進捗を記録しましょう。

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