방법론

전략패턴과 OCP

더즈 2022. 2. 18. 09:49

OCP 개방-폐쇄 원칙 (Open/closed principle)

소프트웨어 요소는 확장에는 열려 있으나 변경에는 닫혀 있어야 한다

이런 거짓말 같은 말이? 확장을 하려면, 당연히 기존 코드를 변경?

다형성을 활용해보자

인터페이스를 구현한 새로운 클래스를 하나 만들어서 새로운 기능을 구현

역할과 구현의 분리

 

우테코 1주 차 미션인 자동차 경주 미션을 생각해보자.

public class Car {

	private int position;
    
	public void move(int movingValue) {
		if (movingValue >= STANDARD_OF_MOVING) {
			position++;
		}
}

자동차가 움직이기 매개변수로 받은 정수가 기준 이상이어야 한다. 현재 이 movingValue의 정책은 0부터 9까지 중 랜덤 한 숫자를 받는 것이다.

 

실제로 쓰이는 곳에선 이렇게 쓰일 것이다.

 

RacingGame 클래스

public RacingResult race(AttemptNumber attemptNumber) {
	// ...
	
    	Random random = new Random();
	do {
		cars.forEach(car -> car.move(random.nextInt(10));
		// ...
	} while (반복 조건);

	// ...
}

이렇게 구현했을 때 문제점이 뭘까? movingValue의 정책이 랜덤한 값이 아니라 다른 정책으로 바뀌면 이 RacingGame의 코드가 변경된다. 여러 정책을 번갈아 써야 할 경우도 마찬가지이다. 이러면 기능이 확장될 때 코드 변경이 불가피하다.

 

때문에 전략패턴을 사용한다. 

public interface MovingStrategy {
	int generate();
}

 

public class RandomMovingStrategy implements MovingStrategy {

	private static final int RANDOM_VALUE_RANGE = 10;

	private static final Random random = new Random();

	@Override
	public int generate() {
		return random.nextInt(RANDOM_VALUE_RANGE);
	}
}

movingValue 생성 전략을 MovingStrategy 인터페이스로 추상화한 뒤 구현체인 RandomMovingStrategy를 RacingGame에서 사용하는 것이다.

 

전략 패턴 적용 후 RacingGame

ublic class RacingGame {

	private final MovingStrategy movingStrategy = new RandomMovingStrategy();

	public RacingResult race(AttemptNumber attemptNumber) {
		// ...

		do {
			cars.forEach(car -> car.move(movingStrategy.generate()));
		} while (반복 조건);

		// ...
	}

RacingGame를 생성할 때 알맞은 구현체만 넣어준다면 RacingGame의 코드 변경 없이 다양한 전략을 사용할 수 있다. 클라이언트 코드의 변경이 거의 없이 새로운 구현체를 만들어(확장)하면 되는 것이다.

 

문제점

  • RacingGame 클라이언트가 구현 클래스를 직접 선택
  • MovingStrategy movingStrategy = new RandomMovingStrategy(); //기존 코드
  • MovingStrategy movingStrategy = new XXXXXXMovingStrategy(); //변경 코드
  • 현 객체를 변경하려면 클라이언트 코드를 변경해야 한다.
  • 분명 전략패턴을 사용했지만 OCP 원칙을 지킬 수 없다.
  • 이 문제를 어떻게 해결해야 하나?
  • 객체를 생성하고, 연관 관계를 맺어주는 별도의 조립, 설정자가 필요하다.

이 문제점은 향후 스프링을 공부하면서 해결해보려고 한다.