정의
이는 다음과 같은 의미를 가지고 있다.
어떤 문제를 해결하는 알고리즘을 클래스 별로 캡슐화해서, 필요에 따라 교체 가능하도록 하여
문제 상황마다 다른 전략(Strategy)을 세울 수 있게 하는 디자인 패턴
디자인 패턴을 접하는 많은 사람들이 그러하듯, 정의만 봐서는 어떤 내용인지 이해가 잘 되지 않는다.
아래와 같은 문제 상황과 예시를 보며 알아보자.
문제 상황
만약 객체 지향 프로그래밍을 이용해 다음과 같은 클래스를 작성해야 한다고 가정해 보자.
- Warrior(전사) 슈퍼 클래스를 만든다. 이 클래스는 공격 기능, 이동 기능이 있다.
- 이를 상속받는 서브 클래스들은 각각 다른 공격 기능, 이동 기능을 갖출 수 있다.
- 공격 기능에는 Slash, Arrow가 있고, 이동 기능에는 Walk, Ride가 있다.
이와 같은 단순한 클래스를 작성해본다고 할 때, 가장 기본적이고 간단하게 생각할 수 있는 방법은 아래와 같을 것이다.
Warrior 라는 슈퍼 클래스를 만들고, 이를 상속 받는 2개의 서브 클래스를 작성하였다.
그리고 각 서브 클래스(Warrior1, Warrior2)는 공격 기능과 이동 기능을 작성할 수 있다.
즉, attack() 메소드에는 Slash, Arrow 중 원하는 것을 구현하고, move() 메소드에는 Walk, Ride 중 원하는 것을 구현한다.
그렇다면 이렇게 클래스를 작성했을 때 문제가 될 수 있는 상황은 무엇일까?
(1) 만약, 사용자가 서브 클래스의 공격 방식 및 이동 방식을 수정하고자 하면 코드 전체를 수정해야 한다.
→ 참고로, 이렇게 객체 지향 프로그래밍에서 코드의 변화가 생기는 것을 OCP(Open-Closed Principle)에 위배된다고 한다.
(2) 또한 서브 클래스를 계속해서 추가하는 과정에서 동일한 공격 방식 및 이동 방식을 사용한다면 코드의 중복이 생긴다.
OCP란, 코드의 변화에는 닫혀있어야 하고, 확장에는 열려있어야 하는 객체 지향의 원칙을 말한다.
이에 대한 해결 방법은 공격 기능, 이동 기능이라는 부분을 묶어서 캡슐화 시켜서
코드의 변화 없이 확장 가능하도록 만들어주는 것이다.
해결 방법
전략 패턴은 어떤 문제를 해결하는 알고리즘을 클래스 별로 캡슐화 한다고 하였다.
그러므로 이와 같은 상황에서는 공격 기능, 이동 기능을 묶어서 캡슐화해야 한다.
Warrior 클래스를 대상으로 아래와 같이 세분화시킬 수 있다.
우선, 공격 기능과 이동 기능을 AttackStrategy, MoveStrategy 라는 인터페이스로 분리했다.
그리고 공격 기능에 대해서는 AttackStrategy 인터페이스를 구현한 SlashStrategy, ArrowStrategy 등으로 나누었다.
Warrior 클래스에서는 이러한 공격, 이동 기능 타입의 변수를 가지게 됨으로써
서브 클래스에서 기능의 변화를 원할 때에는 해당 기능 타입의 인스턴스를 대입함으로써 기능의 변화를 꾀할 수 있으며,
결과적으로는 OCP에 위배되지 않으면서 새로운 서브 클래스의 확장 역시 가져갈 수 있다.
구현
public class Warrior {
public String name;
private AttackStrategy attackStrategy;
private MoveStrategy moveStrategy;
Warrior(String name) {
this.name = name;
}
public String getName() {
return name;
}
public void doAttack() {
System.out.print(name + ": ");
attackStrategy.attack();
}
public void doMove() {
System.out.print(name + ": ");
moveStrategy.move();
}
public void setAttack(AttackStrategy attackStrategy) {
this.attackStrategy = attackStrategy;
}
public void setMove(MoveStrategy moveStrategy) {
this.moveStrategy = moveStrategy;
}
}
public class Warrior1 extends Warrior {
public Warrior1(String name) {
super(name);
}
}
public class Warrior2 extends Warrior {
public Warrior2(String name) {
super(name);
}
}
public interface AttackStrategy {
public void attack();
}
public class SlashStrategy implements AttackStrategy {
public void attack() {
System.out.println("Slash Attack!");
}
}
public class ArrowStrategy implements AttackStrategy {
public void attack() {
System.out.println("Arrow Attack!");
}
}
public interface MoveStrategy {
public void move();
}
public class WalkStrategy implements MoveStrategy {
public void move() {
System.out.println("Move by walk!");
}
}
public class RideStrategy implements MoveStrategy {
public void move() {
System.out.println("Move by ride!");
}
}
public class WarriorTest {
public static void main(String[] args) {
Warrior warrior1 = new Warrior1("Steve");
Warrior warrior2 = new Warrior2("Peter");
warrior1.setAttack(new SlashStrategy());
warrior1.setMove(new WalkStrategy());
warrior1.doAttack();
warrior1.doMove();
warrior2.setAttack(new ArrowStrategy());
warrior2.setMove(new RideStrategy());
warrior2.doAttack();
warrior2.doMove();
warrior2.setAttack(new SlashStrategy());
warrior2.setMove(new WalkStrategy());
warrior2.doAttack();
warrior2.doMove();
}
}
참고
헤드퍼스트 디자인 패턴, 한빛 미디어
'Programming > Design Pattern' 카테고리의 다른 글
[디자인패턴] 데코레이터 패턴(Decorator Pattern)이란 무엇인가? (0) | 2023.03.08 |
---|---|
[디자인패턴] 옵저버 패턴(Observer Pattern)이란 무엇인가? (0) | 2023.03.08 |