스트레티지 패턴(Strategy Pattern)에 대해서 알아보자
환경
- Java
스트레티지 패턴(Strategy Pattern)이란?
스트레티지 패턴(Strategy Pattern)
스트레티지 패턴(Strategy Pattern)
: 특정한 알고리즘을 정의하고 이를 캡슐화하여 해당 알고리즘이 필요한 대상들이 이를 상호 교체 가능하게 하는 구조- Java의 경우 인터페이스를 생성하고 해당 부분을 구현해서 캡슐화 하고 이를 원하는 객체의 메소드에 붙여주면 된다. 자세한 설명과 예제는 아래 참고
- 이미지 출처: https://en.wikipedia.org/wiki/Strategy_pattern
필요성
로봇 사례
- 로봇을 만든다고 생각해보자. 로봇은 태권V도 있고 아톰도 있고 건담도 있다. 이때 어떤 방법으로 만들어야할까? 가장 기본틀인 로봇을 추상 클래스로 만들고 상속을 통해 각각을 만들어보자.
- 우선 로봇 추상 클래스를 만들고 태권V와 아톰만 만들어보자.
예제
Robot
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
public class TaekwonV extends Robot {
public TaekwonV(String name){
super(name);
}
@Override
public void attack() {
System.out.println("kick");
}
@Override
public void move() {
System.out.println("walk");
}
}
Atom
public class Atom extends Robot {
public Atom (String name){
super(name);
}
@Override
public void attack() {
System.out.println("punch");
}
@Override
public void move() {
System.out.println("fly");
}
}
Main
public class Main {
public static void main(String[] args) {
Robot taekwonV = new TaekwonV("TaekwonV");
Robot atom = new Atom("Atom");
System.out.println("I am " + taekwonV.getName());
taekwonV.attack();
taekwonV.move();
System.out.println();
System.out.println("I am " + atom.getName());
atom.attack();
atom.move();
}
}
해당 코드의 문제점
- 기존 로봇의 공격 방법을 수정하려면 새로운 기능을 위해 기존 코드를 수정하기 때문에 OCP(Open-Closed Principle)에 위반된다. 예를들어 위의 예제에서 아톰이 태권V와 똑같은 move()를 사용하려 하면 코드를 직접 수정해야하고 나아가 중복이 발생하며 로봇의 종류가 많아질수록 코드의 관리는 어려워진다.
- 새로운 로봇을 만들어서 아톰과 같은 공격과 태권V와 같은 이동을 하게 하고 싶다면? 동일한 코드를 복사하게 작성하게 되면 중복이 발생하게 된다. 예를 들어서 건담을 만들때 아톰의 attack()과 태권V의 move() 코드를 또 동일하게 작성하기 때문에 중복이 발생한다.
해결 방법
- 로봇을 만드는 과정에 있어서 변화하는 부분이 무엇인지 먼저 찾아야한다. 변화될 수 있는 부분을 찾아서 이를 캡슐화하고 상호 교체 가능하도록 만드는것이 중요하다.
- 예제 코드들에서는 attack()과 move()의 코드들이 계속 변경될 수 있으며 이를 캡슐화해서 필요할때 붙여주면 코드의 중복도 줄이고 코드의 변경도 줄일 수 있다.
- 직접적인 구현은 각각을 interface로 구현해서 객체를 만들때 붙여주면 된다.
- 그림에서 보면 move()의 경우에 MovingStrategy라는 인터페이스를 만들고 각각의 방법에 대한 구현을 implements를 통해서 클래스화 시키면된다.
예제
Robot
public abstract class Robot {
private String name;
private AttackStrategy attackStrategy;
private MovingStrategy movingStrategy;
public Robot(String name) {
this.name = name;
}
public String getName() {
return name;
}
public void attack() {
attackStrategy.attack();
}
public void move() {
movingStrategy.move();
}
public void setAttackStrategy(AttackStrategy attackStrategy) {
this.attackStrategy = attackStrategy;
}
public void setMovingStrategy(MovingStrategy movingStrategy) {
this.movingStrategy = movingStrategy;
}
}
AttackStrategy
public interface AttackStrategy {
public void attack();
}
MovingStrategy
public interface MovingStrategy {
public void move();
}
KickingStrategy
public class KickingStrategy implements AttackStrategy {
@Override
public void attack() {
System.out.println("kick");
}
}
PunchingStrategy
public class PunchingStrategy implements AttackStrategy {
@Override
public void attack() {
System.out.println("punch");
}
}
WalkingStrategy
public class WalkingStrategy implements MovingStrategy {
@Override
public void move() {
System.out.println("walk");
}
}
FlyingStrategy
public class FlyingStrategy implements MovingStrategy {
@Override
public void move() {
System.out.println("fly");
}
}
TaekwonV
public class TaekwonV extends Robot {
public TaekwonV(String name) {
super(name);
}
}
Atom
public class Atom extends Robot {
public Atom(String name) {
super(name);
}
}
Gundam
public class Gundam extends Robot {
public Gundam(String name) {
super(name);
}
}
Main
public class Main {
public static void main(String[] args) {
Robot taekwonV = new TaekwonV("TaekwonV");
Robot atom = new Atom("Atom");
Robot gundam = new Gundam("Gundam");
taekwonV.setAttackStrategy(new KickingStrategy());
taekwonV.setMovingStrategy(new WalkingStrategy());
atom.setAttackStrategy(new PunchingStrategy());
atom.setMovingStrategy(new FlyingStrategy());
gundam.setAttackStrategy(new PunchingStrategy());
gundam.setMovingStrategy(new WalkingStrategy());
System.out.println("I am " + taekwonV.getName());
taekwonV.attack();
taekwonV.move();
System.out.println();
System.out.println("I am " + atom.getName());
atom.attack();
atom.move();
System.out.println();
System.out.println("I am " + gundam.getName());
gundam.attack();
gundam.move();
}
}