Java/Design Pattern

[헤드퍼스트 디자인 패턴] 1. 전략 패턴

noahkim_ 2024. 12. 16. 03:10

에릭 프리먼 님의 "헤드퍼스트 디자인 패턴" 책을 정리한 포스팅 입니다

 

1. 오리 시뮬레이션 게임

  • Duck 클래스를 슈퍼클래스로 생성
  • 각 오리 종류를 서브클래스로 두어 Duck 클래스를 상속함 (MallardDuck, RedheadDuck, RubberDuck)

 

심각한 문제 발생

  • 슈퍼클래스에 추상메서드 추가 시, 모든 서브클래스에서 구현이 강제됨
  • 몇몇 서브클래스에만 적용되어야 할 메서드일 경우, 논리적으로 오류가 발생함

 

예: fly()

더보기
// 상위 클래스
abstract class Duck {
    public abstract void fly();  // 👈 모든 Duck에게 fly() 강제
    public abstract void quack();
}
// MallardDuck는 날 수 있음
class MallardDuck extends Duck {
    public void fly() {
        System.out.println("날아갑니다!");
    }

    public void quack() {
        System.out.println("꽥꽥!");
    }
}

// RubberDuck는 날 수 없음 ❌
class RubberDuck extends Duck {
    public void fly() {
        // ❌ 논리적으로 말이 안 됨
        throw new UnsupportedOperationException("러버덕은 날 수 없습니다.");
    }

    public void quack() {
        System.out.println("삑삑!");
    }
}
  • MallardDuck, RedheadDuck에는 가질 수 있는 행동
  • RubberDuck에는 가져서는 안되는 행동

 

상속 생각하기

상속의 단점
  • 모든 서브클래스에서 구현해야 함
  • 규격이 바뀔 경우 서브클래스에서 계속해서 구현해줘야 함

 

해결책
  • 해당 메서드를 구현할 서브클래스는 구현하고, 나머지는 공백으로 오버라이딩 해두기
  • 결국 모든 서브클래스에서 해당 메서드를 구현해야 해서, 올바른 방법이 아님

 

인터페이스 설계하기

공통적으로 구현해야 하는 명세만 인터페이스로 
  • 상속의 단점을 해결함
  • 그러나, 코드를 재사용하지 않아 코드 관리가 어려움

 

2. 디자인 원칙

  • 행동을 인터페이스 타입으로 추상화: 런타임에서 다양한 구현체 변경 가능

 

명세화

  • 달라지는 부분을 인터페이스로 만듬
  • 인터페이스에 행동을 위임함

 

예) QuackBehavior

더보기
public interface QuackBehavior {	
    void quack();
}

 

Strategy

  • 해당 인터페이스의 구현체(알고리즘군)를 미리 생성
  • 다른 형식의 객체에서도 행동을 재사용할 수 있음

 

예) QuackBehavior

더보기
public class Quack implements QuackBehavior {
    @Override
    public void quack() { System.out.println("꽥꽥"); }
}
public class Squeak implements QuackBehavior {
    @Override
    public void quack() { System.out.println("삑삑"); }
}

 

캡슐화

  • 변할 수 있는 부분(행동)을 고정된 부분(클래스 본체)에서 분리하기
  • 유연성 향상 (바뀌지 않는 부분에 영향이 미치지 않음)

 

예시) Duck

더보기
public abstract class Duck {
    QuackBehavior quackBehavior;
    
    public void performQuack() { quackBehavior.quack(); }
    
    public void setQuackBehavior(QuackBehavior quackBehavior) { this.quackBehavior = quackBehavior; }
}

 

Composition

  • 클래스 내부에 다른 객체(행동)을 포함시켜 조합
  • 상속보다 유연, 동작을 동적으로 변경 가능

 

예시) Duck

더보기
public abstract class Duck {
    FlyBehavior flyBehavior;
    QuackBehavior quackBehavior;
    
    // ...
}