FIF's 코딩팩토리

스트래티지 패턴(Strategy Pattern) 정리 본문

Back-End/Design Pattern(디자인 패턴)

스트래티지 패턴(Strategy Pattern) 정리

FIF 2019. 6. 4. 17:27
반응형

스트래티지 패턴(Strategy Pattern)

 

1.   스트래티지 패턴이란

     A. 행위를 클래스로 캡슐화해 동적으로 행동을 자유롭게 바꿀 수 있게 해주는 패턴

            i. 같은 문제를 해결하는 여러 알고리즘이 클래스별로 캡슐화되어 있고, 이들이 필요할 때 교체할 수

              있도록 함으로써 동일한 문제를 다른 알고리즘으로 해결할 수 있게 하는 디자인 패턴이다.

 

     B. 전략을 쉽게 바꿀 수 있도록 해주는 디자인 패턴이다.

 

     C. 전략이란 어떤 목적을 달성하기 위해 일을 수행하는 방식, 비즈니스 규칙, 문제를 해결하는 알고리즘이다.

 

     D. 특히 게임 프로그래밍에서 게임 캐릭터가 자신이 처한 상황에 따라 공격이나 행동하는 방식을 바꾸고 싶을   경우 스트래티지 패턴은 매우 유용하다.

     E.  역할이 수행하는 작업

            i. Strategy : 인터페이스나 추상 클래스로 외부에서 동일한 방식으로 알고리즘을 호출하는 방법을 명시.

 

            ii. ConcreteStrategy : 스트래티지 패턴에서 명시한 알고리즘을 실제로 구현한 클래스.

 

            iii. Context : 스트래티지 패턴을 이용하는 역할을 수행하고 필요에 따라 동적으로 구체적인 전략을

                바꿀 수 있도록 setter 메소드(‘집약 관계’)를 제공한다.

 

 

2.   예시 : 로봇 만들기

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("나는 미사일 무기를 가지고 있다.");
      }
      @Override
      public void move() {
            System.out.println("나는 걷기밖에 못한다.");
      }
}

 

Atom.java

public class Atom extends Robot {
      public Atom(String name) {
            super(name);
      }
      @Override
      public void attack() {
            System.out.println("나의 무기는 강한 펀치다!");
      }
      @Override
      public void move() {
            System.out.println("나는 날수도 있다!");

 

3.   문제점

     A. 기존 로봇의 공격과 이동 방법을 수정하는 경우

            i. Atom이 날 수는 없고 오직 걷게만 하고 싶은데?

 

            ii. TaekwonV를 날게 하려면?

public class Atom extends Robot {
  public Atom(String name) { super(name); }
  public void attack() { System.out.println("I have strong punch."); }
  public void move() { System.out.println("I can only walk."); } // 수정
}

             iii. 새로운   기능으로 변경하려면, 기존 코드의 내용을 수정해야 하므로

                 OCP(Open-Closed-Principle) 위배됨.

 

             iv. TaekwonV와 Atom의 move() 메서드 내용이 중복된다. 중복된 코드는 많은 문제를 야기시킬 수 있다.

 

             v. 만약 걷는 방식에 문제가 있거나 새로운 방식으로 수정하려면 모든 중복 코드를 일관성 있게 변경해야 한다.

 

     B. 새로운 로봇을 만들어 기존 공격(attack) 또는 이동 방법(move)을 추가/수정             

            i. 새로운 로봇으로 Sungard를 만들어 TaekwonV의 미사일 공격 기능을 추가?

public class Sungard extends Robot {
  public Sungard(String name) { super(name); }
  public void attack() { System.out.println("I have Missile."); } // 중복
  public void move() { System.out.println("I can only walk."); }
}

             ii. TaekwonV와 Sungard 클래스의 attack()메소드 내용이 중복된다.

             iii. 현재 시스템의 캡슐화 단위가 ‘Robot’ 자체이므로 로봇 추가는 매우 쉽다.

             iv. 하지만 새로운 로봇인 ‘Sungard’에 기존의 공격 또는 이동 방법을 추가하거나 변경하려 하면 문제가                           발생한다.

 

4.   해결책

문제 해결을 위해선 무엇이 변화되었는지 찾은 후에 이를 클래스로 캡슐화해야 한다.

     A. 로봇 예제에서 변화되면서 문제를 발생시키는 요인은 로봇의 이동 방식과 공격 방식의

        변화이다.

 

     B. 이를 캡슐화하려면 외부에서 구체적인 이동 방식과 공격 방식을 담은 구체적인

        클래스들을 은닉해야 한다.

            i. 공격 이동을 위한 인터페이스를 각각 만들고 이들을 실제 구현한 클래스를 만들어야 한다.

 

             ii. Robot 클래스가 이동 기능과 공격 기능을 이용하는 클라이언트 역할을 수행

                 1) 구체적인 이동, 공격 방식이 MovingStrategy와 AttackStrategy 인터페이스에 의해 캡슐화되어 있다.

 

                 2) 이 인터페이스들이 일종의 방화벽 역할을 수행해 Robot 클래스의 변경을 차단해준다.

           

             iii. 스트래티지 패턴을 이용하면 새로운 기능의 추가(새로운 이동, 공격 기능)가 기존의 코드에 영향을 미치지 못하게 하므로         OCP를 만족하는 설계가 된다.

                 1) 이렇게 변경된 새로운 구조에서는 외부에서 로봇 객체의 이동, 공격 방식을 임의대로 바꾸도록 해주는                                 setter 메소드가 필요하다.

 

                 2) setMovingStrategy, setAttackStrategy

 

                 3) 이렇게 변경이 가능한 이유는 상속 대신 ‘집약 관계’를 이용했기 때문이다.

 

Robot.java

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;
      }
      public void setName(String name) {
            this.name = name;
      }

 

TaekwonV.java

public class TaekwonV extends Robot {
      public TaekwonV(String name) {
            super(name);
}

 

Atom.java

public class Atom extends Robot {
      public Atom(String name) {
          super(name);
    }
}

                                               

AttackStrategy.java

public interface AttackStrategy {
      public void attack();
}

 

MissileStrategy.java

public class MissileStrategy implements AttackStrategy {
      @Override
      public void attack() {
            System.out.println("나는 미사일을 가졌다!");
      }
}

 

PunchStrategy.java

public class PunchStrategy implements AttackStrategy {
      @Override
      public void attack() {
            System.out.println("나는 강력한 펀치를 가졌다!");
      }
}

 

MovingStrategy.java

public interface MovingStrategy {
      public void move();
}

 

FlyingStrategy.java

public class FlyingStrategy implements MovingStrategy {
      @Override
      public void move() {
            System.out.println("나는 날 수 있어요!");
      }
}

 

WalkingStrategy.java

public class WalkingStrategy implements MovingStrategy {
      @Override
      public void move() {
            System.out.println("나는 걷기밖에 못해요!");
      }
}

 

Client.java

public class Client {
       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("--------------------------------------------");
             System.out.println("내 이름은!" + atom.getName() + " 이지!");
             atom.move();
             atom.attack();
       }
}

 

반응형
Comments