반응형
전략 패턴 (Strategy Patter) 이란
- GoF 디자인 패턴 중에 행위 패턴에 해당되는 전략 패턴은 전략을 쉽게 바꿀 수 있도록 해주는 디자인 패턴이다.
- 여기에서 전략이란 어떤 목적을 달성하기 위해 일을 수행하는 방식, 비즈니스 규칙, 문제를 해결하는 알고리즘 등으로 이해할 수 있다.
- 같은 문제를 해결하는 여러 알고리즘이 클래스별로 캡슐화되어 있고 이들이 필요할 때 교체할 수 있도록 함으로써 동일한 문제를 다른 알고리즘으로 해결할 수 있게 하는 디자인 패턴이다.
- 특히 게임 프로그래밍에서 게임 캐릭터가 지신이 처한 상황에 따라 공격이나 행동하는 방식을 바꾸고 싶을 때 전략 패턴은 매우 유용하다.
전략 패턴 예시
[로봇 만들기]
만들어야 할 로봇 아톰(Atom 클래스)과 태권V(TaekwonV 클래스)이다. 이 두 로봇은 공격 기능과 이동 기능이 있다.
아톰은 공격할 때 주먹만 사용하지만 하늘을 날 수 있다.
태권V는 미사일로 공격할 수 있지만 날아다지니 못하고 걷기만 할 수 있다.
아래 그림은 로봇을 설계할 때 사용하는 클래스 다이어그램이다.
Robot.java
public abstract class Robot {
private String name;
public Robot(String name) {
this.name = name;
}
public String getName() {
return name;
}
public abstract void attack();
public abstract void move();
}
TaekwonV.java
public class TaekwonV extends Robot{
public TaekwonV(String name) {
super(name);
}
@Override
public void attack() {
System.out.println("I have Missile and cat attack with it.");
}
@Override
public void move() {
System.out.println("I can only walk.");
}
}
Atom.java
public class Atom extends Robot{
public Atom(String name) {
super(name);
}
@Override
public void attack() {
System.out.println("I have strong puch and can attack with it.");
}
@Override
public void move() {
System.out.println("I can fly.");
}
}
Solution.java
public class Solution {
public static void main(String[] args) {
Robot taekwonV = new TaekwonV("TaekwonV");
Robot atom = new Atom("Atom");
System.out.println(taekwonV.getName());
taekwonV.move();
taekwonV.attack();
System.out.println(atom.getName());
atom.move();
atom.attack();
}
}
[문제점 1]
- 예를 들어 아톰이 날 수는 없고 오직 걷게만 만들고 싶다면? 또는 태권V를 날게 하려면?
public class Atom extends Robot{
public Atom(String name) {
super(name);
}
@Override
public void attack() {
System.out.println("I have strong puch and can attack with it.");
}
@Override
public void move() {
// 날 수 없도록 수정
System.out.println("I can only walk.");
}
}
- 이는 새로운 기능으로 변경하려고 기존 코드의 내용을 수정해야 하므로 'OCP(Open-Closed Principle) 개방 폐쇄 원칙'을 위반한다.
- OCP(Open-Closed Principle) 개방 폐쇄 원칙이란, 기존의 코드를 변경하지 않으면서 기능을 추가할 수 있도록 설계가 되어야 한다.
- 또한 Atom 클래스의 move 메서드와 TaekwonV 클래스의 move 메서드가 동일한 기능을 실행하므로 중복되는 상황이 발생한다.
- 만약 걷는 방식에 문제가 있거나 새로운 방식으로 수정하려면 모든 중복된 코드를 일관성 있게 변경해야 하므로 유지보수면에서 좋지 않다.
[문제점 2]
- 새로운 로봇을 추가하는 경우, 현재 설계는 로봇 자체가 캡슐화 단위이므로 새로운 로봇을 추가하기가 매우 쉽다.
- 그러나 새로운 로봇에 기존의 공격 또는 이동 방법을 추가하거나 변경하려고 하면 문제가 발생한다.
- 가령 마징가Z 로봇에 태권V의 미사일 공격 기능을 사용하려고하면 TaekwonV 클래스와 attack 메서드가 중복해서 사용된다. 이러한 중복 코드는 나중에 심각한 문제를 일으킨다.
[해결책]
- 로봇 설계에서 문제를 해결하려면 무엇이 변화되었는지 찾아야 한다. 변화된 것을 찾은 후에 이를 클래스로 캡슐화해야 한다.
- 로봇이 변화되면서 문제를 발생시키는 요인은 이동 방식과 공격 방식의 변화다. 즉, 새로운 방식의 이동 및 공격 기능이 계속해서 추가 될 수 있으므로 기존의 로봇이나 새로운 로봇이 이러한 기능을 별다른 코드 변경 없이 제공받거나 기존의 공격이나 이동 방식을 다른 공격이나 이동 방식으로 쉽게 변경할 수 있어야 한다.
- 캡슐화하려면 외부에서 구체적인 이동 방식과 공격 방식을 담은 구체적인 클래스들을 은닉해야 한다.
- 이를 위해 공격과 이동을 위한 인터페이스를 각각 만들고 이들을 실제 실현한 클래스를 만들어야 한다.
- 개선 된 설계에서는 새로운 공격 방식이 개발되어 현재 로봇에 제공하더라도 AttackStrategy 인터페이스가 변화에 대한 일종의 방화벽 역할을 수행해 Robot 클래스의 변경을 차단해준다.
- 즉, 새로운 기능의 추가가 기존의 코드에 영향을 미치지 못하게 하므로 OCP를 만족하는 설계가 된다.
[개선 된 최종 소스 코드]
Robot.java
public abstract class Robot {
private String name;
private MovingStrategy movingStrategy;
private AttackStrategy attackStrategy;
public Robot(String name) {
this.name = name;
}
public String getName() {
return name;
}
public void move() {
movingStrategy.move();
}
public void attack() {
attackStrategy.attack();
}
public MovingStrategy getMovingStrategy() {
return movingStrategy;
}
public void setMovingStrategy(MovingStrategy movingStrategy) {
this.movingStrategy = movingStrategy;
}
public AttackStrategy getAttackStrategy() {
return attackStrategy;
}
public void setAttackStrategy(AttackStrategy attackStrategy) {
this.attackStrategy = attackStrategy;
}
}
MovingStrategy.java / AttackStrategy.java
public interface MovingStrategy {
public void move();
}
public interface AttackStrategy {
public void attack();
}
WalkingStrategy.java / FlyingStrategy.java / MissileStrategy.java / PunchStrategy.java
public class WalkingStrategy implements MovingStrategy {
@Override
public void move() {
System.out.println("I can only walk.");
}
}
public class FlyingStrategy implements MovingStrategy {
@Override
public void move() {
System.out.println("I can fly.");
}
}
public class MissileStrategy implements AttackStrategy{
@Override
public void attack() {
System.out.println("I have Missile and can attack with it.");
}
}
public class PunchStrategy implements AttackStrategy {
@Override
public void attack() {
System.out.println("I have strong punch and can attack with it.");
}
}
Solution.java
public class Solution {
public static void main(String[] args) {
Robot taekwonV = new TaekwonV("TaekwonV");
Robot atom = new Atom("Atom");
taekwonV.setMovingStrategy(new WalkingStrategy());
taekwonV.setAttackStrategy(new MissileStrategy());
atom.setMovingStrategy(new FlyingStrategy());
atom.setAttackStrategy(new PunchStrategy());
System.out.println(taekwonV.getName());
taekwonV.move();
taekwonV.attack();
System.out.println(atom.getName());
atom.move();
atom.attack();
}
}
References
「JAVA 객체 지향 디자인 패턴」 - 한빛미디어
반응형
'디자인 패턴' 카테고리의 다른 글
[Design Pattern] 디자인 패턴 이해 목적 및 사용 이유 (0) | 2021.07.19 |
---|