뱅킹 시스템
Coddy Java 여정의 객체 지향 프로그래밍 섹션에 포함된 레슨 — 87개 중 85번째.
이 챌린지에서는 OOP 원칙을 사용하여 실제 금융 운영을 모델링하는 은행 시스템을 구축하는 과제를 수행합니다.
시스템은 SavingsAccount 및 CheckingAccount와 같은 다양한 계좌 유형을 처리해야 하며, 이들은 모두 기본 Account 클래스를 상속받습니다. 각 계좌 유형은 고유한 동작을 가집니다. 예금 계좌는 이자가 발생할 수 있고, 당좌 예금 계좌는 마이너스 통장 보호 기능이 있을 수 있습니다.
트랜잭션을 모델링하는 방법에 대해 생각해 보세요. Transactable과 같은 인터페이스는 deposit() 및 withdraw()를 위한 메서드를 정의할 수 있습니다. 유효하지 않은 작업을 우아하게 처리하기 위해 InsufficientFundsException과 같은 사용자 정의 예외를 사용하는 것을 고려해 보세요.
Bank 클래스는 여러 계좌와 고객을 관리하며 컴포지션(composition)을 보여줄 수 있습니다. Factory 패턴을 적용하여 다양한 계좌 유형을 생성하거나, Observer 패턴을 사용하여 고객에게 계좌 변경 사항을 알릴 수 있습니다.
잔액 및 계좌 번호와 같은 민감한 데이터를 보호하기 위해 캡슐화에 집중하고, 다양한 계좌 유형을 일관되게 처리하기 위해 다형성을 사용하십시오.
챌린지
쉬움OOP 원칙에 대한 숙련도를 증명할 수 있는 완전한 은행 시스템을 구축해 봅시다! 다양한 계좌 유형, 트랜잭션 처리, 적절한 예외 관리를 포함하는 시스템을 여러 파일에 걸쳐 구성하여 만들게 됩니다.
코드는 다음 7개의 파일로 구성됩니다:
Transactable.java: 금융 작업을 위한 계약을 정의하는 인터페이스를 생성합니다.deposit(double amount)와withdraw(double amount)메서드를 선언해야 하며, 두 메서드 모두 성공 여부를 나타내는 boolean을 반환합니다.InsufficientFundsException.java:Exception을 상속받는 사용자 정의 예외를 생성합니다. 생성자는 시도된 출금 금액과 현재 잔액을 인자로 받아야 하며,Insufficient funds: attempted %.2f, available %.2f형식의 메시지를 생성합니다.Account.java: 모든 은행 계좌를 나타내는 추상 기본 클래스를 생성합니다.accountNumber(String)와balance(double)를 위한 private 필드, 두 필드를 초기화하는 생성자, 적절한 getter, 하위 클래스에서 수정할 수 있도록 balance를 위한 protected setter, 그리고 String을 반환하는 추상 메서드getAccountType()을 포함합니다."[accountNumber] ([accountType]): $[balance]"를 반환하는getDetails()메서드를 구현하며, 잔액은 소수점 2자리까지 포맷팅합니다.SavingsAccount.java:Account를 상속받고Transactable을 구현하는 클래스를 생성합니다.interestRate(double)를 위한 private 필드를 추가합니다. 생성자는 accountNumber, 초기 balance, interestRate를 인자로 받습니다.getAccountType()이"Savings"를 반환하도록 구현합니다.deposit()의 경우 잔액에 금액을 더하고 true를 반환합니다.withdraw()의 경우 금액이 잔액을 초과하면 false를 반환하고, 그렇지 않으면 차감 후 true를 반환합니다. 이자율 백분율만큼 잔액을 늘리는applyInterest()메서드를 추가합니다.CheckingAccount.java:Account를 상속받고Transactable을 구현하는 클래스를 생성합니다.overdraftLimit(double)를 위한 private 필드를 추가합니다. 생성자는 accountNumber, 초기 balance, 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:interestRateCHECKING:accountNumber:balance:overdraftLimitDEPOSIT:accountNumber:amountWITHDRAW:accountNumber:amountTRANSFER:fromAccount:toAccount:amountINTEREST: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]을 출력합니다.
입력으로 쉼표로 구분된 명령 문자열 하나를 받게 됩니다.
예를 들어, 입력이 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 출금을 허용하는 방식에 주목하세요. 이것이 바로 마이너스 한도 보호(overdraft protection)가 작동하는 모습입니다! 저축 예좌(savings account)는 이체 후 잔액에 대해 5%의 이자를 받습니다. 이 챌린지는 상속, 인터페이스, 다형성, 사용자 정의 예외 및 캡슐화를 실제적인 은행 애플리케이션으로 결합한 것입니다!
치트 시트
은행 시스템(Banking System)은 계좌 관리, 트랜잭션 및 예외 처리를 통해 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));
}
}구성(Composition)
클래스는 다른 객체들의 컬렉션을 관리할 수 있습니다:
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: 소수점 둘째 자리까지 포맷팅하여 "Total deposits: $[total]" 출력
}
}
이 레슨에는 짧은 퀴즈가 포함되어 있습니다. 레슨을 시작해 문제를 풀고 진행 상황을 기록하세요.
객체 지향 프로그래밍의 모든 레슨
4상속
상속의 기초 (extends)super 키워드메서드 오버라이딩 (@Override)생성자 체이닝Object 클래스단일 및 다중 레벨 상속다중 클래스 상속이 불가능한 이유요약 - 직원 계층 구조