디자인 패턴

[디자인 패턴] Strategy Pattern (전략 패턴) - 직관적인 설명 & 예시

Shong Studio 2024. 3. 23. 15:45
728x90
반응형

안녕하세요 Shong Studio입니다.

 

디자인 패턴 중 Behavior(행동) Interface를 만들어서 상속관계에 있는 클래스들의 복잡도를 전략적으로 줄여주는 "Strategy Pattern"에 대해서 알아보도록 하겠습니다.

Strategy Pattern 이란?

전략 패턴를 직관적으로 정의하자면,

추상클래스 내에 정의된 Behavior(행동) function을 다양한 Behavior(행동) 을 케어해주기 위해서 Behavior(행동) Interface를 만들어서 확장성있는 구조를 만드는 디자인 패턴입니다.

Strategy Pattern을 사용해야하는 이유는?

  1. 교체 가능한 행동: Strategy Pattern을 사용하면 구체적인 행동들을 클래스로 캡슐화하여, 이를 실행 시점에 교체할 수 있습니다. 이는 다양한 알고리즘을 쉽게 실험하고 변경할 수 있게 해줍니다.
  2. 확장성: 새로운 전략(알고리즘)을 추가하더라도 기존 코드를 변경할 필요가 없으며, 단지 새로운 클래스를 추가하기만 하면 됩니다.
  3. SOLID 원칙 준수: 특히, 개방/폐쇄 원칙(OCP)과 의존성 역전 원칙(DIP)을 준수합니다. 기존 코드를 변경하지 않고도 행동을 확장할 수 있으며, 고수준 모듈이 저수준 모듈에 의존하지 않도록 합니다.
  4. 복잡도 감소: Strategy Pattern을 사용하면 복잡한 조건문 대신에 전략을 쉽게 교체할 수 있어 코드의 복잡도를 줄일 수 있습니다.

Strategy Pattern을 어떻게 사용하는지 알아보자.

예제로 많이 사용되는 Duck 클래스를 사용해서 설명하겠습니다.

 

FlyBehavior Interface를 만들어서 Duck이라는 추상클래스 안에 선언을 해줬는데요.

 

사실 기본적으로 public fly() {} 와 같이 fly 함수를 선언하여 Duck을 상속받는 하위 클래스 내에서 예외적인 fly 행동에 대한 구현을 진행해도 됩니다.

 

하지만 Concrete class에 fly하는 예외적인 행동에 대해 직접적으로 다시 작성하는 것은 관리 차원에서 어렵고 또한 예외적으로 분류했던 특정 fly action이, 추가되는 duck들의 fly action과 동일하다면 똑같은 코드를 다시 작성하게 되며 수정되는 요구사항에 대하여 유지보수 할 때 복잡성이 증가합니다.

 

이를 방지하기 위해 "전략"적으로 행동(behavior)에 대한 Interface를 새로 만들어서 관리하는 디자인 패턴을 사용합니다.

public abstract class Duck {
    FlyBehavior flyBehavior;

    public Duck() {}

    public void setFlyBehavior(FlyBehavior fb) {
        flyBehavior = fb;
    }

    public void performFly() {
        flyBehavior.fly();
    }

    // 기타 Duck 관련 메소드
}

 

FlyBehavior 인터페이스를 만들었고 Flywithwings, FlyNoway 클래스가 FlyBehavior을 상속합니다

public interface FlyBehavior {
    void fly();
}
public class FlyWithWings implements FlyBehavior {
    public void fly() {
        System.out.println("날고 있어요!");
    }
}

public class FlyNoWay implements FlyBehavior {
    public void fly() {
        System.out.println("저는 못 날아요.");
    }
}

★중요★ :

현재 두가지 클래스에 대해서 예시를 들었지만 Fly의 action이 난다 or 못난다만 있으면 굳이 전략 디자인패턴을 사용할 필요는 없습니다.

 

But

fly하는 action이 "비행기를 타고 난다", "로켓을 타고 난다", "독수리를 타고 난다" 등 여러 fly action이 추가될 가능성이 있는지 개발 요구사항을 잘 확인하고 판단하여 적용하는 것이 좋습니다.

 

구체적인 Duck 클래스 생성

Duck 클래스를 상속받는 구체적인 Duck 클래스(Concrete class)를 만듭니다.

예를 들어, MallardDuckRubberDuck 클래스를 만들고 생성자에서 각각의 날기 행동을 설정합니다.

public class MallardDuck extends Duck {
    public MallardDuck() {
        flyBehavior = new FlyWithWings();
    }

    // MallardDuck에 특화된 메소드
}

public class RubberDuck extends Duck {
    public RubberDuck() {
        flyBehavior = new FlyNoWay();
    }

    // RubberDuck에 특화된 메소드
}

 

이제 Duck 객체를 만들고, 각각의 날기 행동을 실행해 볼 수 있습니다.

public class DuckSimulator {
    public static void main(String[] args) {
        Duck mallard = new MallardDuck();
        mallard.performFly(); // "날고 있어요!"

        Duck rubberDuck = new RubberDuck();
        rubberDuck.performFly(); // "저는 못 날아요."
    }
}

 

이와 같이 Strategy Pattern을 사용하면 실행 중에 객체의 행동을 쉽게 변경할 수 있으며, 행동을 추가하거나 변경하기 위해 기존 코드를 수정할 필요가 없습니다. 이를 통해 코드의 유연성과 확장성을 크게 향상시킬 수 있습니다.

728x90
반응형