전략 패턴

SeokHwan An·2023년 4월 4일
0

디자인패턴

목록 보기
3/3

전략 패턴을 처음부터 알고 이용한 것은 아니었습니다. 이번 우테코 자동차 경주 미션을 진행하던 중에 다음과 같은 요구조건이 있었습니다. 자동차가 전진하는 조건은 0에서 9사이에서 random 값을 구한 후 random 값이 4 이상일 경우 전진하고, 3 이하의 값이면 멈춘다. 이 부분을 구현하는 것은 어렵지 않았습니다.

public class Car {

		private final String name;
		private final int position;
		...

		public void goForward() {
				Random random = new Random();
				int randomNumber = random.nextInt(10);
				if (randomNumber >= MOVABLE_MIN_NUMBER) {
            position++;
        }		
	}
}

이제부터가 문제의 시작이었습니다. 이렇게 되면 테스트 코드를 제어할 수 있는 것인가? 랜덤의 숫자를 뽑아서 하는 것인데 항상 통과하는 테스트를 작성할 수 있는 것인가? 다양한 의문점에서 내린 결론은 다음과 같습니다. 숫자를 생성하는 인터페이스를 만들고 하나는 랜덤으로 생성하는 구현체, 또 하나는 고정적으로 값을 만들어내는 구현체를 만드는 것이었습니다. 프로덕트 코드에서는 랜덤으로 생성하는 구현체를 이용하고 테스트 상황에서는 고정적으로 값을 만들어 내는 구현체를 이용하면 테스트 시 제어할 수 없는 부분을 제어할 수 있습니다.

public interface NumberGenerator {
		
		int generate();
}

public class RandomNumberGenerator implements NumberGenerator {

		@Override
    public int generate() {
				Random random = new Random();
        return random.nextInt(RANDOM_NUMBER_MAX_RANGE);
    }
}

public class FixNumberGenerator implements NumberGenerator {

		@Override
		public int generate() {
				return 5;
		}
}
public class Car {
		
		private final NumberGenerator numberGenerator;
		private final String name;
		private final int position;
		...

		public void goForward() {
				if (numberGenerator.generate() >= MOVABLE_MIN_NUMBER) {
            position++;
        }		
		}
}

이를 기반으로 테스트 코드를 작성하면 이동하는 상황을 쉽게 테스트할 수 있었습니다.

public class CarTest {
		
		@Test
		@DisplayName("숫자가 4이상이 나오면 자동차는 전진한다.")
		void car_goForward_when_number_4_or_more() {
				// given
				FixNumberGenerator fixNumberGenerator = new FixNumberGenerator();
				Car car = new Car("car", fixNumberGenerator);
			
				// when
				car.goForward();

				// then
				assertThat(car.getPosition()).isEqualTo(1);
		}

}

다시 돌아와서 이와 같이 인터페이스를 만들고 각 상황에 맞는 구현체를 이용하는 방식이 전략 패턴이라고 크루들로부터 들었습니다. 이후 전략패턴에 대해 학습 기회가 생겼고 제가 느낀 전략패턴에 대해서 정리해보려고 합니다.

전략패턴이란

전략 패턴이란 유사항 행위들을 캡슐화 하는 인터페이스를 정의하고, 이를 구현하는 전략 클래스를 생성하여 객체의 행위를 동적으로 바꾸고 싶을 때 행위를 직접 수정하는 것이 아닌 전략을 바꿔주기만 함으로써 행위를 유연하게 확장하는 방법입니다.

위에서 구현한 방법을 UML로 표현하면 다음과 같습니다.

Car객체는 NumberGenerator(interface)에 의존을 하고 있기에 Random 형식의 전략과 Fix 형식의 전략에 직접적으로 의존을 하지 않아도 바꿔 사용하면서 유연하게 확장 및 활용이 가능합니다. 이는 인터페이스의 장점인 다형성이 극대화 된 것이라고 볼 수 있습니다.

전략패턴을 이용해보면서 느낀점

  1. 프로그램 확장의 용이성

전략패턴의 가장 큰 장점이라고 생각합니다. 다양한 이동전략이 생긴다고 할 때 코드의 수정이 필요하지 않고 새로운 전략을 만들어서 원하는 방식의 전략으로 바꿔끼우면 되기 때문에 프로그램 확장이 편리하다는 것을 느꼈습니다.

  1. 테스트 코드 작성의 편의성

위의 상황에서 볼 수 있듯이 테스트 코드를 작성하는데 편리함을 제공해 줍니다. 우리가 제어할 수 없는 부분(랜덤 값)을 제어할 수 있게 구현(고정된 값)이 가능해 테스트를 보다 용의 하게 관리할 수 있다는 것을 느낄 수 있었습니다.

전략패턴에 대해 생각해볼것

  1. 많은 리소스의 발생

프로그램 확장의 용이성이 좋아진 만큼 전략이 많아지게 된다면 많은 리소스(전략 클래스)가 생성되며 클래스가 많아지게 된다면 유지보수를 하는데 많은 시간이 필요해질 것입니다.

전략패턴이 이용되는 곳

전략 패턴은 다양한 곳에서 활용되고 있습니다. 우리가 자주 사용하는 List가 전략 패턴의 한 예로 볼 수 있습니다. List의 구현체인 LinkedList와 ArrayList, Vector 등 다양한 구현제 각각이 List의 전략으로 볼 수 있습니다. 이 뿐만 아니라 모듈 개발은 전략 패턴이 주로 이용되기 때문에 모듈을 확장하는 것이 유리하다는 것을 알 수 있습니다.

0개의 댓글