수업 복습

다형성 복습 및 과제

_김영인 2026. 1. 13. 17:57

 

1. Phone 인터페이스 – 추상화의 시작점

 

Phone은 인터페이스다.
코드는 “어떤 휴대폰이든 반드시 가져야 할 기능”만 정의한다.

핵심 포인트는 다음이다.

  • 실제 구현은 모른다
  • 무엇을 할 수 있는지만 약속한다
  • 구현체가 바뀌어도 기준은 변하지 않는다

이 단계에서 이미 중요한 변화가 생긴다.

구체 클래스가 아니라 역할 (기능) 에 의존하기 시작한다.


 

2. GalaxyPhone / IPhone – 다형성의 실체

 

GalaxyPhone과 IPhone은 Phone을 구현한 클래스다.

  • 같은 메서드를 가지고 있다
  • 내부 동작은 서로 다르다
  • 하지만 사용하는 쪽에서는 차이를 알 필요가 없다

이것이 다형성이다.

  • turnOn()이라는 메서드를 호출하지만
  • 실제 실행 결과는 객체에 따라 달라진다

이 구조 덕분에 다음이 가능해진다.

  • 새로운 휴대폰 추가 가능
  • 기존 코드 수정 최소화
  • 기능 확장에 유리한 구조

 

3. Client 

 

Client는 휴대폰을 사용하는 쪽이다.

다형성을 적용했지만 아직 문제가 남아 있다.

 

문제의 핵심은 이것이다.

  • 객체를 직접 생성하고 있다
  • new GalaxyPhone() 같은 코드가 존재한다

이 상태의 문제점은 다음과 같다.

  • 휴대폰이 바뀌면 Client 코드도 수정해야 함
  • 객체 생성 방식이 고정됨
  • 테스트와 확장이 불리함

다형성은 적용했지만 결합도는 아직 완전히 낮아지지 않았다.


 

4. BeanFactory – 객체 생성 책임 분리

 

이 문제를 해결하기 위해 등장한 것이 BeanFactory다.

BeanFactory의 역할은 단 하나다.

객체를 대신 생성해주는 것

 

여기서 중요한 변화가 발생한다.

  • Client는 더 이상 new를 쓰지 않는다
  • “어떤 객체가 필요한지”만 요청한다
  • “어떻게 생성되는지”는 알지 못한다

이로 인해 얻는 효과는 다음과 같다.

  • 객체 생성 책임이 한 곳으로 모인다
  • Client와 구현체의 결합도가 크게 감소한다
  • 구조 변경 시 수정 범위가 줄어든다

이 패턴이 바로 팩토리 패턴이다.


 

5. 구조 변화 한눈에 보기

 

기존 구조

  • Client가 객체 생성
  • 구현 클래스에 직접 의존
  • 변경에 취약

 

개선된 구조

  • Client → BeanFactory → 객체
  • Client는 추상 타입만 의존
  • 구현 교체가 쉬움

이 구조는 단순히 “코드 예제”가 아니라 스프링으로 넘어가기 위한 매우 중요한 징검다리다.


 

6. IoC (제어의 역전)로 이어지는 흐름

 

지금 구조에서 이미 중요한 변화가 일어났다.

  • 객체 생성 제어권이 Client에서 분리됨
  • 생성과 사용의 책임이 분리됨

이 상태에서 한 단계만 더 나아가면 다음과 같다.

  • BeanFactory를 개발자가 직접 만들지 않는다
  • 객체 생성과 관리를 프레임워크가 담당한다

 

이것이 바로 스프링에서 말하는 IoC (제어의 역전) 다.

개발자는 객체를 생성하지 않는다
컨테이너가 객체를 생성하고 관리한다
개발자는 필요한 객체를 요청해서 사용한다


 

스프링은 갑자기 등장한 기술이 아니라 기존 객체지향 설계의 문제를 체계적으로 해결한 결과물이다.


 

  • Phone → 추상화
  • GalaxyPhone / IPhone → 다형성
  • Client → 사용 책임
  • BeanFactory → 생성 책임 분리
  • 다음 단계 → Spring Container

 

1. Payment 인터페이스 

package example.payment;

public interface Payment {

    void pay(int amount);

}
 
  • “결제 수단이라면 반드시 해야 할 행동”만 정의
  • 구현 방식은 알 필요 없음

 

2. CardPayment 

 
package example.payment;

public class CardPayment implements Payment {

    @Override
    public void pay(int amount) {
        System.out.println("카드로 " + amount + "원 결제합니다.");
    }

}

 

3. BankTransferPayment

 
package example.payment;

public class BankTransferPayment implements Payment {

    @Override
    public void pay(int amount) {
        System.out.println("계좌이체로 " + amount + "원 결제합니다.");
    }

}
  • 같은 pay() 메서드
  • 다른 내부 동작
  • 다형성 완성

 

4. BeanFactory 

package example.payment;

public class BeanFactory {

    public static Payment getPayment(String type) {

        if ("card".equals(type)) {
            return new CardPayment();
        }

        if ("bank".equals(type)) {
            return new BankTransferPayment();
        }

        return null;
    }

}

 

여기서 핵심:

  • new가 Client에서 사라짐
  • 객체 생성 책임이 한 곳으로 모임
  • 팩토리 패턴 적용

 

5. Client 

 
package example.payment;

public class Client {

    public static void main(String[] args) {

        Payment payment = BeanFactory.getPayment("card");
        payment.pay(50000);

        Payment payment2 = BeanFactory.getPayment("bank");
        payment2.pay(30000);
    }

}

 

Client의 특징:

  • 구현 클래스 이름을 모른다
  • Payment 타입만 알고 있다
  • 결제 방식이 바뀌어도 코드 수정 최소화

 

1. 추상화

  • Payment 인터페이스만 의존

2. 다형성

  • 같은 pay() 호출
  • 객체에 따라 다른 결과

3. 결합도 감소

  • Client는 구현체 변경에 영향 없음

4. 팩토리 패턴

  • 객체 생성 책임 분리

'수업 복습' 카테고리의 다른 글

객체지향 → IoC → Spring Container 흐름  (0) 2026.01.14
Spring DI (의존성 주입)  (1) 2026.01.14
Spring Framework  (1) 2026.01.13
커스텀 태그 복습  (0) 2025.12.01
팩토리 패턴 & 싱글톤 복습  (0) 2025.11.28