전략패턴은 객체들이 할 수 있는 행위 각각에 대해 전략 클래스를 생성하고, 유사한 행위들을 캡슐화하는 인터페이스를 정의하여, 객체의 행위를 동적으로 바꾸고 싶은 경우 직접 행위를 수정하지않고, 전략을 바꿔주기만 함으로써 행위를 유연하게 확장하는 방법을 말한다.
1. 전략 패턴의 사용 이유
예) 몬스터(Enemy), 캐릭터(Character) 클래스가 있고, 이 두 클래스는 IMovable 인터페이스를 구현했다고 가정합니다.
그리고 Enemy와 Character 객체를 사용하는 Client도 있다.
이 구조를 코드로 표현하면 다음과 같다.
public interface IMovable {
public void move();
}
public class Enemy implements IMovable{
public void move(){
System.out.println("땅으로 이동");
}
}
public class Character implements IMovable{
public void move(){
System.out.println("바다로 이동");
}
}
public class Client {
public static void main(String args[]){
IMovable enemy = new Enemy();
IMovable character = new Character();
enemy.move();
character.move();
}
}
해당 인터페이스를 상속받은 Enemy과 Character에서 move 함수를 구현해주었다. 그러면 enemy에 있는 동작을 바꾸려면
public void move(){
System.out.println(" 땅으로 이동 -> 수정.");
}
이렇게 바꾸면 될 것이다. 하지만 이러한 수정 방식은 SOLID 원칙 중 개방-패쇄원칙에 위배 된다.
그래서 기존의 move() 를 수정하지 않으면서 행위가 수정되어야한다.
또한 지금과 같은 방식의 변경은 시스템이 확장이 되었을 때 유지보수를 어렵게 합니다.
예를 들어, 몬스터와 같이 몬스터의 이동을 땅에서도 이동할 수 있고, 바다로도 갈 수 있고, 하늘로도 이동 할 수 있다. 이러한 것들이 여러개가 추가된다고 했을 때, 모두 몬스터와 같이 move() 메서드를 사용합니다. 그래서 다른 여러개가 추가되고, 수정이 필요할 때는 하나씩 고쳐야하며, 메서드의 중복이 생깁니다.
즉, 지금과 같은 수정 방식의 문제점은 다음과 같습니다.
- OCP(개방-패쇄원칙) 위배
- 시스템이 커져서 확장이 될 경우 메서드의 중복문제 발생
따라서 이를 해결하고자 전략 패턴을 사용하려고 합니다.
2. 전략 패턴 구현
이번에는 위와 같이 선로를 따라 이동하는 버스가 개발된 상황에서 시스템이 유연하게 변경되고 확장될 수 있도록 전략 패턴을 사용해보도록 하겠습니다.
이동하는 몬스터가 개발된 상황에서 시스템이
1)
먼저 전략을 생성하는 방법입니다.
현재 운송수단은 선로를 따라 움직이든지, 도로를 따라 움직이든지 두 가지 방식이 있습니다.
즉, 움직이는 두 방식에 대해 Strategy 클래스를 생성하도록 합니다. ( RailLoadStrategy, LoadStrategy )
현재 이동수단은 땅을 통해서 이동하거나, 바다를 통해서 이동하는 두 가지 방식이 있습니다.
GroundStrategy, SeaStrategy
그리고 두 클래스는 move() 메서드를 구현하여, 어떤 방법으로 움직이는지에 대해 구현합니다.
또한 두 전략 클래스를 캡슐화 하기 위해 MovableStrategy 인터페이스를 생성합니다.
이렇게 캡슐화를 하는 이유는 운송수단에 대한 전략 뿐만 아니라,
다른 전략들이 추가적으로 확장되는 경우를 고려한 설계입니다.
이를 코드로 표현하면 다음과 같습니다.
public interface IMovableStrategy {
public void move();
}
public class GroundStrategy implements IMovableStrategy{
public void move(){
System.out.println("땅으로 이동");
}
}
public class SeaStrategy implements IMovableStrategy {
public void move(){
System.out.println("바다로 이동");
}
}
2)
다음으로 이동 수단에 대한 클래스를 정의할 차례입니다.
캐릭터와 몬스터 같은 이동 수단은 move() 메서드를 통해 움직일 수 있습니다.
그런데 이동 방식을 직접 메서드로 구현하지 않고, 어떻게 움직일 것인지에 대한 전략을 설정하여, 그 전략의 움직임 방식을 사용하여 움직이도록 합니다.
그래서 전략을 설정하는 메서드인 setMovableStrategy()가 존재합니다.
public class Moving(){
private IMovableStrategy _movableStragegy;
public void move(){
_movableStrategy.move();
}
public void SetMovableStrategy(IMovableStrategy movableStrategy){
this._movableStrategy = movableStrategy;
}
}
public class enemy extends Moving(){
}
public class character extends Moving(){
}
3)
이제 enemy과 character 객체를 사용하는 Client를 구현할 차례입니다.
enemy 과 character 객체를 생성한 후에, 각 이동 수단이 어떤 방식으로 움직이는지 설정하기 위해 setMovableStrategy() 메서드를 호출합니다.
그리고 전략 패턴을 사용하면 프로그램 상으로 로직이 변경 되었을 때, 얼마나 유연하게 수정을 할 수 있는지 살펴보기 위해 몬스터도 바다로 이동할 수 있게 개발되었다는 상황을 만들어 몬스터의 이동 방식 전략을 수정했습니다.
public class Client {
public static void main(String args[]){
Moving enemy = new Enemy();
Moving character = new Character();
enemy.setMovableStrategy(new GroundStrategy());
character.setMovableStrategy(new SeaStrategy());
enemy.move();
character.move();
//----------아래 수정--------------------
enemy.setMovableStrategy(new SeaStrategy());
enemy.move(); //바다로도 이동
}
}
이상으로 전략 패턴이 무엇인지 알아봤습니다.
'디자인패턴' 카테고리의 다른 글
유니티 - Strategy Pattern (스킬 편) (0) | 2025.04.27 |
---|---|
MVC, MVP, MVVM 비교 (0) | 2025.04.15 |
State Pattern (FSM) (0) | 2024.05.01 |
Observer Parttern (0) | 2024.04.30 |
Singleton Pattern (0) | 2024.04.27 |