스택큐힙리스트

상속·다형성으로 ATM 시뮬레이터 확장 본문

카테고리 없음

상속·다형성으로 ATM 시뮬레이터 확장

스택큐힙리스트 2025. 7. 8. 16:03
반응형

목표 🔑

  • 상속(Inheritance) 으로 계좌 타입 확장하기
  • 다형성(Polymorphism) 으로 코드 의존도 낮추기
  • ATM 시뮬레이터 구조 설계 & 구현
  • OCP·LSP 같은 OOP 원칙 체감하기

1. 왜 ‘계좌’를 계층 구조로 나눌까?

은행 업무는 계좌 종류마다 규칙이 다릅니다. 예금·적금·마이너스통장 등 조건이 다른 로직을 하나의 클래스에 몰아넣으면 수정·추가 시마다 위험한 분기문이 폭증합니다.

  • 상속 으로 공통 로직을 BankAccount 추상 클래스로 뽑아두면,
  • 다형성 으로 SavingsAccount, CheckingAccount 를 하나의 타입으로 다룰 수 있어 확장성가독성 이 모두 좋아집니다.

2. 기본 뼈대: 추상 클래스 & 인터페이스

public abstract class BankAccount {
    protected String owner;
    protected long   balance;

    public BankAccount(String owner, long balance) {
        this.owner   = owner;
        this.balance = balance;
    }
    public void deposit(long amount)      { balance += amount; }
    public abstract void withdraw(long amount);
    public long getBalance()              { return balance; }
}

public interface Transferable {
    void transfer(BankAccount target, long amount);
}
  • 공통 로직(입금, 잔액 조회)은 추상 클래스에,
  • 부가기능(계좌 간 이체)은 인터페이스로 분리 👉 필요 계좌만 구현.

3. 구현 계좌 클래스 — 규칙만 다르게!

// 자유입출금
public class CheckingAccount extends BankAccount implements Transferable {
    public CheckingAccount(String owner, long balance) {
        super(owner, balance);
    }
    @Override
    public void withdraw(long amount) {
        if (amount > balance) throw new IllegalStateException("잔액 부족");
        balance -= amount;
    }
    @Override
    public void transfer(BankAccount t, long amt) {
        withdraw(amt);
        t.deposit(amt);
    }
}

// 적금(출금·이체 제한)
public class SavingsAccount extends BankAccount {
    private final double interestRate = 0.03;

    public SavingsAccount(String owner, long balance) {
        super(owner, balance);
    }
    @Override
    public void withdraw(long amount) {
        throw new UnsupportedOperationException("적금은 중도 출금 불가");
    }
    public void addInterest() {
        balance += (long) (balance * interestRate);
    }
}
  • 각 클래스는 필요한 부분만 오버라이딩 → OCP 만족
  • 타입은 BankAccount 로 통일 → LSP·다형성 구현

4. ATM 시뮬레이터 핵심 로직

public class ATM {
    private final Map<String, BankAccount> accounts = new HashMap<>();

    public void register(BankAccount account) { accounts.put(account.owner, account); }

    public void run() {
        Scanner sc = new Scanner(System.in);
        while (true) {
            System.out.println("[1]입금 [2]출금 [3]잔액 [4]이체 [0]종료");
            int cmd = sc.nextInt();  // switch 문으로 제어

            switch (cmd) {
                case 1 -> depositFlow(sc);
                case 2 -> withdrawFlow(sc);
                case 3 -> balanceFlow(sc);
                case 4 -> transferFlow(sc);
                case 0 -> { System.out.println("안녕히 가세요!"); return; }
                default -> System.out.println("잘못된 입력");
            }
        }
    }
    // ... 각 Flow 메서드는 계좌 객체를 다형적으로 사용
}
  • UI(콘솔)와 비즈니스 로직 분리
  • switch 문으로 메뉴 선택, 내부에서는 계좌 타입 구분 없이 동작

5. 확장 시 이점 ✨

  1. 새 상품(예: 외화통장) 추가 ⇒ 새 클래스를 extends BankAccount 로 한 줄만 등록
  2. 기존 ATM 코드는 수정 無 → OCP 실천
  3. 테스트 코드에서 List<BankAccount> 로 전체 케이스 순회 가능
  4. 스프링으로 전환 시 @Component 달아 DI 로 주입

6. 실습 체크리스트 ✔️

  • 추상 클래스·인터페이스 선언
  • SavingsAccount, CheckingAccount 각각 오버라이딩
  • 다형성 활용: 리스트·맵에 BankAccount 타입으로 저장
  • 예외 상황 JUnit5 테스트 작성
반응형
Comments