Menu
Coddy logo textTech

Банковская система

Часть раздела Объектно-ориентированное программирование путешествия по Java на Coddy — урок 85 из 87.

В рамках этого задания вам предстоит создать Банковскую систему, которая моделирует реальные финансовые операции, используя принципы ООП.

Ваша система должна обрабатывать различные типы счетов, такие как SavingsAccount и CheckingAccount, оба из которых расширяют базовый Account класс. Каждый тип счета имеет уникальное поведение: сберегательные счета могут приносить проценты, в то время как текущие счета могут иметь защиту от овердрафта.

Подумайте о том, как моделировать транзакции. Интерфейс, такой как Transactable, может определять методы для deposit() и withdraw(). Рассмотрите возможность использования пользовательских исключений, таких как InsufficientFundsException, для корректной обработки недопустимых операций.

Класс Bank может управлять несколькими счетами и клиентами, демонстрируя композицию. Вы могли бы применить паттерн Factory для создания различных типов счетов или использовать паттерн Observer для уведомления клиентов об изменениях в счете.

Сосредоточьтесь на инкапсуляции для защиты конфиденциальных данных, таких как балансы и номера счетов, и используйте полиморфизм для единообразной обработки различных типов счетов.

challenge icon

Задание

Легко

Давайте создадим полноценную банковскую систему, которая продемонстрирует ваше мастерство владения принципами ООП! Вы создадите систему с различными типами счетов, обработкой транзакций и правильным управлением исключениями — и все это будет организовано в нескольких файлах.

Вы распределите свой код по семи файлам:

  • Transactable.java: Создайте интерфейс, определяющий контракт для финансовых операций. Он должен объявлять методы deposit(double amount) и withdraw(double amount), оба из которых возвращают логическое значение для индикации успеха.
  • InsufficientFundsException.java: Создайте пользовательское исключение, расширяющее Exception. Конструктор должен принимать запрашиваемую сумму снятия и текущий баланс, создавая сообщение в формате "Insufficient funds: attempted %.2f, available %.2f".
  • Account.java: Создайте абстрактный базовый класс, представляющий любой банковский счет. Включите приватные поля для accountNumber (String) и balance (double), конструктор, инициализирующий оба поля, соответствующие геттеры, защищенный (protected) сеттер для баланса (чтобы подклассы могли его изменять) и абстрактный метод getAccountType(), возвращающий String. Реализуйте метод getDetails(), который возвращает "%s (%s): $%.2f" с балансом, отформатированным до 2 знаков после запятой.
  • 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() разрешите снятие, если сумма меньше или равна сумме баланса и лимита овердрафта; верните false, если она превышает этот лимит.
  • Bank.java: Создайте класс, который управляет счетами с помощью ArrayList. Включите методы:
    • addAccount(Account account) — добавляет счет в банк
    • findAccount(String accountNumber) — возвращает Account или null, если счет не найден
    • transfer(String fromAccount, String toAccount, double amount) — переводит деньги между счетами, выбрасывая InsufficientFundsException, если на исходном счете недостаточно средств. При успехе возвращает "Transferred $%.2f from %s to %s"
    • 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 $%.2f to %s%n"
    • WITHDRAW: "Withdrew $%.2f from %s%n" при успехе или "Withdrawal failed: " + accountNumber
    • TRANSFER: Выведите сообщение об успехе или "Transfer failed: " + e.getMessage()
    • INTEREST: "Interest applied to " + accountNumber

    После всех команд выведите "--- Bank Summary ---", затем детали каждого счета (используя getDetails()) и, наконец, "Total deposits: $%.2f%n", отформатированные до 2 знаков после запятой.

Вы получите один входной параметр: строку команд, разделенных запятыми.

Например, при входных данных 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

Обратите внимание, как чековый счет позволяет снять $600, несмотря на то, что на балансе всего $500 — это работа защиты от овердрафта! Сберегательный счет начисляет 5% годовых на свой баланс после перевода. Эта задача объединяет наследование, интерфейсы, полиморфизм, пользовательские исключения и инкапсуляцию в реалистичное банковское приложение!

Шпаргалка

Банковская система демонстрирует принципы ООП через управление счетами, транзакции и обработку исключений.

Интерфейсы для контрактов

Определите общие операции с помощью интерфейсов:

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
    }
}

Инкапсуляция

Защищайте конфиденциальные данные с помощью приватных полей и контролируемого доступа через геттеры/сеттеры. Используйте модификаторы 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: Выведите "Total deposits: $[total]", отформатировав до 2 знаков после запятой
    }
}
quiz iconПроверьте себя

В этом уроке есть небольшой тест. Начните урок, чтобы ответить на вопросы и сохранить прогресс.

Все уроки раздела Объектно-ориентированное программирование