# 전략 (Strategy) 패턴

# 전략패턴이란 무엇인가요?

개념

여러 알고리즘을 캡슐화 하고 상호 교환 가능하게 만드는 패턴.

  • 객체들이 할 수 있는 행위 각각에 대해 전략 클래스를 생성하고,
  • 유사한 행위들을 캡슐화하는 인터페이스를 정의하여,
  • 객체의 행위를 동적으로 바꾸고 싶은 경우 직접 행위를 수정하지 않고 전략을 바꿔주기만 함으로써
  • 행위를 유연하게 확장하는 방법.

image

  • Context : 원래의 로직을 수행하던 클래스
  • ConcreteStrategy : 각각의 알고리즘들이 구현된 클래스
  • Strategy : 추상화된 알고리즘 수행 인터페이스

# 전략 패턴을 언제 사용해야할까요?

상황가정

'무궁화꽃이 피었습니다'를 하는 코드가 있다고 하자. "무궁화 꽃이~"까지는 파란 불. "피었습니다!"까지는 빨간 불이다.
해당 클래스는 speed를 가지는데, speed마다 "무궁화꽃이" 혹은 "피었습니다"를 말하는 속도가 다르다. (speed가 높을 수록 빠름)

public class BlueLightRedLight {
    private int speed;

    public BlueLightRedLight(int speed) {
        this.speed = speed;
    }

    public void blueLight() {
        if (speed == 1) {
            System.out.println("무 궁 화    꽃   이");
        } else if (speed == 2) {
            System.out.println("무궁화꽃이");
        } else {
            System.out.println("무광꼬치");
        }

    }

    public void redLight() {
        if (speed == 1) {
            System.out.println("피 었 습 니  다.");
        } else if (speed == 2) {
            System.out.println("피었습니다.");
        } else {
            System.out.println("피어씀다");
        }
    }
}
public class Client {
    public static void main(String[] args) {
        BlueLightRedLight blueLightRedLight = new BlueLightRedLight(3);
        blueLightRedLight.blueLight();
        blueLightRedLight.redLight();
    }
}

그렇다면

만약에 여기서 스피드가 추가된다면 어떻게 될까??

직접 blueLight(), redLight() 내부를 수정해야 하므로, SOLID의 원칙 중 OCP( Open-Closed Principle )에 위배된다.

# 전략패턴을 적용해보자

public interface Speed {
    void blueLight();
    void redLight();
}
  • 먼저 Strategy인터페이스를 정의한다.
public class Fastest implements Speed {
    @Override
    public void blueLight() {
        System.out.println("무광꼬치");
    }

    @Override
    public void redLight() {
        System.out.println("피어씀다.");
    }
}
public class Faster implements Speed {
    @Override
    public void blueLight() {
        System.out.println("무궁화꽃이");
    }

    @Override
    public void redLight() {
        System.out.println("피었습니다.");
    }
}
public class Normal implements Speed {
    @Override
    public void blueLight() {
        System.out.println("무 궁 화    꽃   이");
    }

    @Override
    public void redLight() {
        System.out.println("피 었 습 니  다.");
    }
}
  • 각각의 ConcreteStrategy인 Slower(speed가 1), Normal(speed가 2), Faster(speed가 3)를 구현한다.
public class BlueLightRedLight {
    public void blueLight(Speed speed) {
        speed.blueLight();
    }

    public void redLight(Speed speed) {
        speed.redLight();
    }
}
  • 원래의 Context에서 실행할 때, 단순히 해당 전략에 대한 메소드만 수행하도록 한다. 간단!!
import java.util.Collections;
import java.util.Comparator;

public class Client {
    public static void main(String[] args) {
        BlueLightRedLight game = new BlueLightRedLight();
        game.blueLight(new Normal()); // 무 궁 화   꽃 이
        game.redLight(new Fastest()); // 피어씀다.
    }
}
  • 사용측에서는 전략을 지정하여 바로 사용할 수 있다.

# 전략 패턴의 장단점은 무엇일까?

장점

  • 새로운 전략을 추가하더라도 기존 코드를 변경하지 않는다.
  • 상속 대신 위임을 사용할 수 있다.
  • 런타임에 전략을 변경할 수 있다.

단점

  • 복잡도가 증가한다.
  • 클라이언트 코드가 구체적인 전략을 알아야 한다

# 전략 패턴의 예시는 무엇일까?

  1. 자바의 Comparator
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;

public class JavaSample {
    public static void main(String[] args) {
        List<Integer> numbers = new ArrayList<>();
        numbers.add(10);
        numbers.add(5);
        System.out.println(numbers); // 10 5

        Collections.sort(numbers, Comparator.naturalOrder());
        System.out.println(numbers); // 5 10
    }
}
  1. Spring의 ApplicationContext
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.context.support.FileSystemXmlApplicationContext;

public class SpringSample {
    public static void main(String[] args) {
        ApplicationContext applicationContext = new ClassPathXmlApplicationContext();
        ApplicationContext applicationContext1 = new FileSystemXmlApplicationContext();
        ApplicationContext applicationContext2 = new AnnotationConfigApplicationContext();
    }
}

# 출처

  • https://victorydntmd.tistory.com/292
  • https://kingchan223.tistory.com/324