우아한테크코스/테코톡
전략패턴
newwisdom
2021. 8. 6. 13:55
반응형
2021-05-24 글
[10분 테코톡] 📣 완태의 전략패턴을 들으며 정리한 글입니다.
if - else의 문제점
example
public double calculate(boolean isFirstGuest, boolean isLastGuest, List<Item> items) {
double sum = 0;
for (Item item : items) {
if (isFirstGuest) {
sum += item.getPrice() * 0.9;
} else if (!item.isFresh()) {
sum += item.getPrice() * 0.8;
} else if (isFirstGuest) {
sum += item.getPrice() * 0.8;
} else {
sum += item.getPrice();
}
}
return sum;
}
기능이 추가될 경우
- 새로운 조건문을 직접 추가해주어야 한다.
- 때문에 추가되는 기능들이 많아질 수롣 코드 블럭도 점점 증가한다.
- 이럴 경우 유지보수성이 떨어진다.
누락될 위험
- (안 좋은 예시이지만) 같은 기능을 하는 로직을 서로 다른 클래스에서 사용하기 위해 복붙을 했을 경우, 한 쪽의 기능이 변경되면 이를 복붙한 모든 클래스를 전부 수정해주어야 한다.
때문에 if-else를 사용하면 유지보수가 어렵다.
OCP - Open Close Principle (개방 폐쇄의 원칙)
소프트웨어 구성요소(컴포넌트, 클래스, 모듈, 함수)는 확장에 대해서는 개방되어야 하지만 변경에 대해서는 폐쇄되어야 한다.
기존 코드를 변경하지 않으면서 기능을 추가할 수 있도록 설계되어야 한다.
기존 코드의 변경이 작으며, 확장이 쉽다는 장점이 있다.
적용 방법
- 상속(is-a)
- 컴포지션(has-a)
컴포지션
변경(확장)될 것과 변하지 않을 것을 구분한다.
이 두 모듈이 만나는 지점에 인터페이스를 정의한다.
구현에 의존하기보다 정의한 인터페이스에 의존하도록 코드를 작성한다.
예시
- List와 ArrayList의 관계
- 타입 선언은 List이지만 인스턴스를 생성할 때는 그 구현체인 ArrayList로 생성한다.
전략 패턴
- 디자인 패턴 중 가장 많이 쓰임
- 디자인 패턴이란? (소프트웨어) 디자인 + (공통적으로 마주치는 문제를 해결하는 방법의) 패턴
- 동적으로 전략 수정 가능
- 행위를 클래스로 캡슐화해 동적으로 행위를 자유롭게 바꿀 수 있게 함
- 새로운 기능의 추가가 기존 코드에 영향을 미치지 못하게 하므로 OCP를 만족
- 기존 코드 변경없이 행위를 자유롭게 바꿀 수 있게 해주는 OCP를 준수한 디자인 패턴
➕ GoF의 디자인 패턴에서의 전략패턴
- 동일 계열의 알고리즘군을 정의하고 (walk, run, fly, rocket)
- 각 알고리즘을 캡슐화하며(MoveStrategy)
- 이들을 상호교환이 가능하도록 만든다.
전략이란?
어떤 목적을 달성하기 위해 일을 수행하는 방식.
비즈니스 규칙, 문제를 해결하는 알고리즘 등 (Random, Reverse, Nothing)
구성
Context
- 전략 패턴을 이용하는 역할을 수행
- 필요에 따라 동적으로 구체적인 전략을 바꿀 수 있도록 함 (setter 또는 DI)
Strategy
- 인터페이스나 추상 클래스로 외부에서 동일한 방식으로 알고리즘을 호출하는 방법 명시
ConcreateStrategy
- 전략패턴에서 명시한 알고리즘을 실제로 구현한 클래스
Example
배민 로봇 이동 전략 예시
- Walk
- Run
- Fly
- Rocket
초기 배달 로봇
public class Robot {
public void display() {
System.out.println("배달 로봇");
}
public void move() {
System.out.println("걸어서 배달합니당.");
}
}
그런데 이제 달리는 로봇도 추가하게 되었다면,
public class RunningRobot {
public void display() {
System.out.println("배달 로봇");
}
public void move() {
System.out.println("뛰어서 배달합니당.");
}
}
이후에 날으는 로봇, 로켓으로 배달하는 로봇이 생기는 경우를 생각해보자.
이를 먼저 상속으로 해결하는 경우, 아래와 같이 구현할 수 있다.
그런데 여기서 로봇에 온도 조절 기능을 추가할 경우, 아래와 같이 구현할 수 있다.
🤔 문제점?
- 메서드 수정이 어렵다.
- "걸어서 배달하는 로봇"이 "빠르게 걸어서 배달합니다"로 변할 경우 모든 클래스를 순회하면서 메서드들을 수정해주어야 한다.
- 새로운 기능 추가가 어렵다.
- 한국어, 영어, 중국어를 말하는 기능이 추가된다면 추상 클래스에 말하기 기능이 추가될 것이고, 이를 상속받는 클래스에 기능에 따라 다른 각각의 메서드를 구현해주어야 한다.
이를 해결하는 것이 바로 전략패턴이다!
- 이동전략 : 걷기, 달리기, 날기 등의 로봇의 행위를 정의한다.
- 온도 전략 : cold, warm, hot등을 정의한다.
로봇을 생성할 때 각각의 전략을 주입해주어 로봇은 전략의 행위를 실행하게 된다.
이렇게 되면 문제점이 어떻게 해결될까?
- 메서드 수정이 용이하다.
- "걸어서 배달하는 로봇"이 "빠르게 걸어서 배달합니다"로 변할 경우 walk 부분만 변경해주면 된다.
- 새로운 기능 추가가 용이하다.
- 한국어, 영어, 중국어를 말하는 기능이 추가된다면 이에 따른 새로운 전략의 행위를 정의해주고, 로봇을 생성할 때 전략을 함께 주입해주면 된다.
- 만약 이동방식이 도중에 변경된다면?
- setter등을 통해 객체 생성 후 전략을 변경할 수 있다.
JDK에서의 전략패턴
Comparator
@FunctionalInterface
public interface Comparator<T> {
int compare(T o1, T o2);
}
Comparator가 전략이고 개발자마다 custom하게 정의한 Comparator구현을 sort()
라는 컨텍스트에서 사용하고 있다.
개발자는 상황에 맞게 비교 전략을 구현할 수 있게 하여 기존 코드의 수정없이 확장을 이룰 수 있다.
참고 자료
반응형