전략 패턴(Strategy Pattern)은 무엇인가?

Hyunta·2022년 2월 21일
0

자동차 경주 미션

목록 보기
7/7
 public void startEachRace() {
        for (Car car : cars) {
            car.move(makeRandom());

startEachRace 메서드를 어떻게 테스트할 것인가? 에 대한 고민으로 부터 시작됐다.

  1. 오버로딩
 public void startEachRace() {
        for (Car car : cars) {
            car.move(makeRandom());

 public void startEachRace(int minBound, int maxBound) {
        for (Car car : cars) {
            car.move(makeRandom(minBound, maxBound));

오버로딩을 통해서 테스트할 때는 랜덤값을 생성하는 범위를 특정지어서 해결했다. 하지만 테스트를 위해 프로덕션 코드를 수정했으니 불필요한 로직이 생겼다. 아래와 같은 피드백을 받게되었다.

테스트를 위한 코드에 관해
a. 테스트를 위해 애플리케이션에 코드가 추가됐다면 이 코드도 테스트 해야 할까? 만약 이걸 테스트하기 위해 또다른 코드가 추가된다면 이것도 테스트해야 할까? 라는 질문을 던져볼 수 있겠네요. 물론 테스트를 위해 추가하는 코드가 복잡하지는 않을 수 있겠지만 추가되고 나면 언제든 애플리케이션에서 사용할 수 있다는 점도 염두에 둬야겠죠.

b. 테스트를 위해 코드를 추가하는 것은 가급적 피해야 한다고 생각합니다. 질문주신 경우는 테스트를 위해 테스트 대상(Cars)의 상태나 환경을 내가 원하는 임의의 상태나 환경으로 바꿔야 하는 경우인데요. Random에 관한 코드를 분리함으로써 테스트가 용이해진 것처럼 Car 컬렉션을 관리할 책임을 별도의 클래스에 위임하고, 자동차 경주와 관련된 로직을 수행할 때 이 컬렉션을 통해 필요한 객체를 가져다 쓴다는 아이디어에서 시작해보시면 어떨까요?

  1. 메서드 시그니처 수정
 public void startEachRace(int minBound, int maxBound) {
        for (Car car : cars) {
            car.move(makeRandom(minBound, maxBound));

public class GameController{
	...
    startEachRace(RANDOM_MIN_BOUND, RANDOM_MAX_BOUND)
    ...
}

메서드 시그니처를 수정하여 컨트롤러에서 최소,최대 범위를 받아서 랜덤값을 생성할 수 있도록 만들었다. 이 코드에 문제점은 컨트롤러가 랜덤값을 생성한다는 사실을 알고 있고, 외부에서 랜덤값의 범위를 설정해줘야한다는 점이다. 테스트를 위해서 프로덕션 코드를 수정한게 되버렸다.

상수 이름에 RANDOM이 있는 것으로 보아서는, GameController는 숫자가 랜덤으로 생성된다는 내부 로직을 알고 있는 걸까요?

숫자의 생성 범위를 외부에서 제어할 수 있어야 할까요?
혹시 테스트를 위한 것은 아닐까요?
자동차 경주 피드백을 참고하셔서 숫자를 생성하는 부분을 분리해보시면 좋겠어요.

어떻게 해결을 해야할지 모르겠어서 찾아보다가 전략패턴에 대해서 알게되었다.


전략패턴(Strategy Pattern)

객체가 할 수 있는 행위들 각각을 알고리즘으로 만들어 놓고, 동적으로 행위의 수정이 필요한 경우 알고리즘을 바꾸는 것만으로 행위의 수정이 가능하도록 만든 패턴.

쉽게 말하자면, 할인 정책이 있고 10%로 할인하는 전략A, 1000원을 할인하는 전략B를 만들어둔 다음 필요에 의해서 전략을 가져다 쓰는 패턴이라 생각하면 된다.


    public void startEachRace(NumberGenerator numberGenerator) {
        for (Car car : cars) {
            car.move(numberGenerator.generateNumber());
        }
    }

랜덤값을 만드는 랜덤값 생성기를 특정 전략이라 생각하고, 숫자 생성기 인터페이스를 구현할 수 있다. 프로덕션 코드에서는 랜덤값을 생성하는 전략을 사용하고, 테스트할 때는 테스트 기능에 맞는 구현체를 만들어서 해결할 수 있다.

public class RandomNumberGenerator implements NumberGenerator{
    private static final int MIN_BOUND = 0;
    private static final int MAX_BOUND = 9;

    @Override
    public int generateNumber() {
        Random random = new Random();
        return random.nextInt(MAX_BOUND - MIN_BOUND + 1) + MIN_BOUND;
    }
}

public class MovableNumberGenerator implements NumberGenerator {
    @Override
    public int generateNumber() {
        return 4;
    }
}

위와 같이 구현하게 된다면, 다형성의 장점을 이용하여 테스트에 편리한 구현체로 만들어 확인할 수 있다.

Reference

https://victorydntmd.tistory.com/292
https://tecoble.techcourse.co.kr/post/2020-05-17-appropriate_method_for_test_by_interface/

profile
세상을 아름답게!

0개의 댓글