본문 바로가기
개발자 준비/디자인패턴 & 아키텍쳐

전략패턴(Strategy Pattern)

by osul_world 2021. 12. 29.
728x90

전략패턴(HAS-A의 장점)


  • 알고리즘의 군을 정의하고 캡슐화해주며, 서로 언제든지 바꿀 수 있도록 해줌

  • 느슨한 연결이 가능하고 동적 변화에 유리하게 설계가능

  • 다형성을 보장 추가와 변형에 유리함

  • HAS-A 와 DIP 의존관계 주입을 이용

    객체 생성에 필요한 다양한 알고리즘을 필요할때 실행시간에 바꾸어 사용하고싶다면?

    생성을 담당하는 메서드를 캡슐화하고 has-a와 dip를 이용한다.

    내부로직을 들어내지 않고 은닉화 할수있으며 동적처리가 가능

     

아래 코드에서 fly() 기능을 사용하고싶다. interface? 상속?

//부모 
abstract class Duck{
    void quack(){ //공통 사용 메서드
        ..
    }
    
    abstract void color(); //자손에게 각자 재정의 위임
}

//중간 클래스
class YellowDuck extends Duck{
    void color(){
        sout("하위 노란 오리들을 위한 중간 클래스");
    }
}

//자손 1
class MDuck extends Duck{
    ...
}

//자손2
class GDuck extends Duck{
    ...
}

상속의 문제: 부모에 정의된 모든 메소드가 모든 자식에 적합하지 않을 수 있음

자손에서 재정의 하는 방식으로 해결

대부분의 자손에서 적합하지 않으면 abstract 메서드로 재정의를 서브클래스(자손)에게 위임

동일하게 해결하는 서브클래스들이 있다면 중간 클래스 도입

인터페이스의 문제: 구현클래스마다 재정의해야함으로 코드중복을 피할수 없음

특정기능을 제공하는 클래스와 아닌 클래스를 구분할수있다.

인터페이스 자체에 메서드를 정의하더라도 오버라이딩 없이 동적 변화를 제공하기 힘들다.

 

전략패턴으로 해결! : 동적변화기법

[전략패턴 관계도]

image

Strategy 인터페이스: 전략을 이용하기 위한 인터페이스 제공 Concrete Strategy 클래스: 다수가 제공되며, 구체적인 전략이구현되어 있는 클래스 Client 클래스(context 클래스): 전략을 실제 활용하는 클래스

[사용시기]

관련있는 클래스들이 행위만 다를 경우 fly()

알고리즘의 다양한 변형이 필요한 경우

[장점]

포함관계(has-a)와 다형성, 추상화에 느슨한 의존을 이용하여 동적 변화를 제공하면서 코드 중복을 피할수있다.

실행시간에 DI를 변경할수있다.

객체의 생성 라이프사이클을 조절가능

새로운 오리타입의 생성이 용이해진다.

새로운 행위의 추가가 쉽다

[단점]

전략이 클라이언트의 상태가 필요한경우 양방향 의존관계가 성립되어 위험하다.

[관련 패턴]

전략 클래스 객체가 불필요하게 많이 생성되는 것을 피하기 위해 싱글톤으로 구현하는 경우가 많음

싱글톤이 아니라면 호출마다 객체 생성

image

  • 각 행위들을 인터페이스로 구분하여 정의한다.
  • 구현클래스들은 포함관계로 객체들을 생성자 주입 받아서 사용한다.
//추상클래스 부모 
abstract class Duck{
    //has-a 이용
    private FlyBehavior fb;
    private QuackBehavior qb;

    //생성자 DI: 추상클래스도 생성자가 있어야한다!!!!!!!!!!!!
    /**
    1. 자식에게 오버라이딩을 강제하기 위해 미완성으로 선언하는것뿐
    2. 생성자가 존재한다. 자손에서 생성자 super를 사용할수있다.
    3. 직접 생성은 불가 //미완성 메서드 존재할가능성 있기 때문
    */
    public Duck(FlyBehavior fb,QuackBehavior qb){
        this.fb=fb;
        this.qb=qb;
    }
    //주입된 구현클래스를 활용
    void quack(){ 
        qb.quack();
    }

    abstract void color(); 

}

//자손
class MDuck extends Duck{
    //부모가  abstract:  자손에서 주입
    public MDuck(FlyBehavior fb,QuackBehavior qb){
        super(fb,qb);
    }
}

//main
/**
자유롭게 DI하여 갈아 끼울수있다.
*/
public class main {
    public static void main(String[] args) {
        FlyBehavior fb = new FlyNoWay(); //다형성 활용: 실행시간에 언제든 갈아끼울수있다.
        QuackBehavior qb = new Squeak();

        Duck d = new MDuck(fb,qb); //DI

        d.quack(); // "sqeak!!"
    }
}

보통 Has-a는 is-a보다 좋다

  • 비교적 유연한 해결책을 가져다 줌
  • 실행시간에 행위변경 가능

 

관련 리펙토링

image

  • 상황에 맞게 선택하여 사용

image

  • 클라이언트의 크기를 줄일수있다.
728x90

'개발자 준비 > 디자인패턴 & 아키텍쳐' 카테고리의 다른 글

장식자 패턴(Decorator)  (0) 2021.12.29
관찰자(Observer) 패턴  (0) 2021.12.29
[번외] UML 이해하기  (0) 2021.12.29
OOP 설계 원리  (0) 2021.12.29
OOP 개요  (0) 2021.12.29