본문 바로가기
카테고리 없음

[디자인 패턴] 전략 패턴 (Strategy Pattern)

by 재영(ReO) 2023. 2. 28.

우아한테크코스 BE 5기 과정 레벨 1의 자동차 경주 미션을 진행하며 전략 패턴을 학습할 기회가 있었다.

 

 

전략 패턴이란?


전략 패턴(strategy pattern)은 객체의 행위를 동적으로 바꾸고 싶은 경우, 직접 행위를 수정하지 않고 전략을 바꿔주기만 함으로써 행위를 유연하게 확장할 수 있는 패턴이다. 객체들이 할 수 있는 행위 각각에 대해 전략 클래스를 생성하고, 유사한 행위들을 캡슐화하는 인터페이스를 정의함으로써 구현한다. 간단히 말하자면, 객체가 할 수 있는 행위들 각각을 전략으로 만들어 놓고, 동적으로 행위의 수정이 필요한 경우 해당하는 전략으로 변경하여 행위의 수정이 가능하도록 만든 패턴이다.

출처 : https://velog.io/@y_dragonrise/%EB%94%94%EC%9E%90%EC%9D%B8-%ED%8C%A8%ED%84%B4-%EC%A0%84%EB%9E%B5-%ED%8C%A8%ED%84%B4Strategy-Pattern

  • Strategy : 인터페이스나 추상 클래스로 외부에서 동일한 방식으로 알고리즘을 호출하는 방법을 명시
  • ConcreateStrategy1, 2, 3 : 스트래티지 패턴에서 명시한 알고리즘을 실제로 구현한 클래스
  • Context : 스트래티지 패턴을 이용하는 역할 수행. 필요에 따라 동적으로 구체적인 전략을 바꿀 수 있도록 setter 메서드 제공

위 도식에서 볼 수 있듯, 어떤 객체의 행위를 바꾸기 위해서는 context에서 strategy의 구현체들인 ConcreateStrategy1,2,3 중 하나를 선택해주기만 하면 된다.



전략 패턴 사용으로 얻을 수 있는 것


전략 패턴의 정의에서 살펴봤듯이, 전략 패턴을 사용하면 우선 객체의 행위를 객체 내부에서 직접 수정할 필요가 없어진다. 

출처 : https://v.daum.net/v/20220625094201639?f=p

위 도식은 22년 새로 시행된 도로교통법 개정안에 대한 설명이다.

전방 차량신호가 녹색인 경우, 우회전 시 횡단보도에 보행자가 있을 때 자동차의 행위를 의사코드로 나타낸다면 다음과 같다.

public 자동차 컨트롤러() {

    자동차가 전진한다();
    
    자동차가 우회전한다() {
    
    	if (보행자가 없다면) {
            전진한다();
            return;
        }
        
        if (보행자가 있다면) {
            일시정지한다();
            
            if (보행자가 다 건너면) {
            	서행한다();
            }
        }
    };
}

슬프게도, 내 자동차는 자율주행이 가능한 데슬라가 아니라서 스스로 우회전을 하거나 속도를 줄이거나 하지 못한다.
보행자가 있는 상황을 판단하고 결정하는 일이 온전히 운전자인 내 일이다.

각박한 서울 시내 운전이 너무 피곤해진 나는 영끌을 해서 구형 데슬라를 구입했다.
편안히 자율 주행을 즐기고 있었는데 아래와 같은 상황이 생겼다.

출처 : 유튜브 "한문철 TV"

 

보행자가 없어서 우회전을 하던 데슬라가 직진하는 차량을 인식하고 멈추지 못해서 접촉사고가 났다.

 

억울한 마음에 코드를 뜯어보니 아래와 같았다.

public class DeslaController {
	Car desla = new Desla();
    public void turnRight() {
        
        desla.goStraight();

        turnRight();
    }

    private void turnRight() {
        if (보행자가 없다) {
            desla.goStraight();
            return;
        }

        if (보행자가 있다) {
            while (보행자가 아직 횡단보도에 있다) {
                desla.stop();
            }

            desla.goSlow();
        }
    }
}

역시나 전진하는 차량이 있는지를 인식하는 조건이 빠져있었다.

 

씩씩거리며 아래와 같이 업데이트를 진행했다.

public class DeslaController {
	Car desla = new Desla();
    public void turnRight() {
        
        desla.goStraight();

        turnRight();
    }

    private void turnRight() {
        if (보행자가 없다 || 전진하는 차량이 없다) {
            desla.goStraight();
            return;
        }

        if (보행자가 있다 || 전진하는 차량이 있다) {
            while (보행자가 아직 횡단보도에 있다 || 차량이 지나가고 있다) {
                desla.stop();
            }

            desla.goSlow();
        }
    }
}

휴, 앞으로 우회전 시에는 사고가 나지 않을 것 같다.

그런데 이번에는 아래와 같은 사고가 났다.

출처 : https://gifsf.com/gif/4516562

전방에서 좌회전하는 차량을 인식하지 못하고 우회전 하던 멍청한 데슬라를 다시 뜯어서 업데이트를 해야하는 상황이 된 나는 그만 정신이 아득해져버렸다.

매번 turnRight()의 내부까지 찾아가서 조건을 추가하려니, 짜증이 이만저만이 아니다. 조금 더 간편하게 업데이트를 할 수 있는 방법은 없을까?


이러한 상황일 때 전략패턴이 도움이 될 수 있다.

public class DeslaController {
	Car desla = new Desla();
    public void turnRight() {
        
        desla.goStraight();

        turnRight(new UpdatedTurnRightStrategy());
    }

    private void turnRight(TurnRightStrategy turnRightStrategy) {
        turnRightStrategy.turnRight()
    }
}
public interface TurnRightStrategy {
	public void turnRight();
}
public class UpdatedTurnRightStrategy implements TurnRightStrategy {
    public void turnRight() {
    	개쩔게 업데이트 된 우회전 전략
    }
}

이렇게 전략 패턴을 사용한다면, DeslaController 내부의 turnRight() 메서드를 매번 수정할 필요가 없어진다.
보행자와 직진하는 차량과 전방에서 좌회전하는 차량과 강아지와 고양이와 고라니 등등에 대한 조건이 추가되면 TurnRightStrategy 인터페이스의 구현체들(ex. UpdatedTurnRightStrategy, Updated_수정_TurnRightStrategy, Updated_최종_TurnRightStrategy, Updated_진짜_최종_TurnRightStrategy etc.)을 갈아끼워주기만 하면 된다.

 

이와 같이 객체의 행위를 동적으로 바꾸고 싶은 경우, 직접 행위를 수정하지 않고 전략을 바꿔주기만 함으로써 행위를 유연하게 확장할 수 있다.