본문 바로가기

디자인 패턴

전략 패턴 (Strategy Pattern) 이란, 개념 및 예시

반응형

전략 패턴 (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 객체 지향 디자인 패턴」 - 한빛미디어

반응형