1. Strategy Pattern

Created: Oct 08, 2019 10:27 AM
Tags: strategy pattern,전략 패턴

[ Strategy Pattern ]

  • : 자주 변동이 생기거나 기존 구조에 새로 추가될 경우 해당 알고리즘을 "캡슐화"해 주입함으로써

  • 클라이언트와는 독립적으로 알고리즘을 교환할 수 있게되는 디자인 패턴

    public abstract class Animal {

      // 각 객체에서 공통으로 사용될 이름 변수
      String name;
    
      public Animal(String name) {
          this.name = name;
      }
    
      public void doBehavior() {
          // Dog, Eagle, Penguin 각 객체 모두 공통으로 동일한 내용으로 호출되는 메서드
          doEat();
    
          // 각 동물 객체별로 재구현이 필요한 이동관련 메서드
          doMove();
    
          // 각 동물 객체별로 재구현이 필요한 울음소리 메서드
          doCry();
    
          if(canFly()) { // hook method로 각 객체에서 재구현해 실행여부를 결정(optional)
              doFly();
          }
      }
    
      public void doEat() {
          System.out.println(name + "(은) 얌얌 밥을 먹습니다.");
      }
    
      public abstract void doMove();
    
      public abstract void doCry();
    
      public boolean canFly() {
          return false;
      }
    
      public abstract void doFly();

    }

    public class Dog extends Animal{

      public Dog(String name) {
          super(name);
      }
    
      @Override
      public void doMove() {
          System.out.println(name + "(은) 네발로 기어갑니다.");
      }
    
      @Override
      public void doCry() {
          System.out.println(name + "(은) 왈왈 짓습니다.");
      }
    
      @Override
      public void doFly() {
          // TODO Auto-generated method stub
      }

    }

    public Eagle(String name) {

          super(name);
      }
    
      @Override
      public void doMove() {
          System.out.println(name + "(은) 날아갑니다.");
      }
    
      @Override
      public void doCry() {
          System.out.println(name + "(은) 쒱~ 하고 웁니다.");
      }
    
      @Override
      public void doFly() {
          System.out.println(name +"(은) 날게가 있어 날 수 있습니다.");
      }
    
      // hook method를 true로 바꿔 doFly()가 동작할 수 있도록 한다.
      @Override
      public boolean canFly() {
          return true;
      }

    }

    public class Penguin extends Animal{

      public Penguin(String name) {
          super(name);
      }
    
      @Override
      public void doMove() {
          System.out.println(name + "(은) 뒤뚱뒤뚱 걷습니다.");
      }
    
      @Override
      public void doCry() {
          System.out.println(name + "(은) 펭귄~하고 웁니다.");
      }
    
      @Override
      public void doFly() {
          System.out.println(name + "(은) 날개가 없어 날 수는 없습니다.");
      }
    
      @Override
      public boolean canFly() {
          return true;
      }

    }

    public class CommonModelTest {

      private static final Logger logger = LoggerFactory.getLogger(CommonModelTest.class);
    @Test
    public void test1() {
        Dog dog = new Dog("강아지");
        dog.doBehavior();

        Eagle eagle = new Eagle("독수리");
        eagle.doBehavior();

        Penguin penguin = new Penguin("펭귄");
        penguin.doBehavior();
        /*
          강아지(은) 얌얌 밥을 먹습니다.
            강아지(은) 네발로 기어갑니다.
            강아지(은) 왈왈 짓습니다.
            독수리(은) 얌얌 밥을 먹습니다.
            독수리(은) 날아갑니다.
            독수리(은) 쒱~ 하고 웁니다.
            독수리(은) 날게가 있어 날 수 있습니다.
            펭귄(은) 얌얌 밥을 먹습니다.
            펭귄(은) 뒤뚱뒤뚱 걷습니다.
            펭귄(은) 펭귄~하고 웁니다.
            펭귄(은) 날개가 없어 날 수는 없습니다.
         * */
    }    


}

위의 코드는 Dog, Eagle, Penguin 동물 객체의 행동을 구현하였는데, 동물의 공통 동작을 정하기 위해서 Template Method Pattern을 이용해서 구성하였다.

  1. 먼저, Dog, Eagle, Penguin은 공통적으로 doEat 밥을 먹는 메서드를 Animal에 구현하였고
  2. 울음소리를 나타내는 doCry는 Animal을 상속받는 각 동물객체에서 재정의하도록 하였다.
  3. 마지막으로 doFly는 강아지는 날 수 없음으로 Optional하게 선택할 수 있도록 Hook Method인 canFly()를 넣어 각 동물객체에서 재정의하여 doFly를 수행할지 여부를 지정할 수 있도록 하여 큰 틀을 구성하였다.

→ 이때, doFly 메서드의 구현방식을 변경하고(날 수 있는 녀석, 없는 녀석)

→ doLive라는 어디에 서식하는지에 대한 메서드를 추가한다고 했을 때

위의 코드의 문제점은 무엇일까?

—> 1. 먼저, doFly 메서드는 Dog한테는 해당사항이 없음에도 override하고 있는 문제점

  1. doFly의 상세 구현 내용을 변경하기 위해 각 객체별로 접근해서 구현 내용을 일일이 바까주어

    야 하는 점.(지금은 객체가 3개뿐이지만, doFly메서드를 사용하는게 20,30개가 넘는다면?)

→ 따라서, 이럴 경우에는 doFly메서드 알고리즘을 별도로 "캡슐화"해서 주입하도록 해 언제든지 알고리즘을 교환하는 방식을 통해 클라이언트 실행단과의 종속성을 제거할 수 있다.

수정 방향)

  1. doFly메서드에 대해 "날수 있는", "날수 없는"의 알고리즘을 분리한다.
  2. 각 객체에 주입한다.

공통사항을 관리하는 Animal 추상 클래스 : 아까와 동일하다.

public abstract class Animal {
    // 각 객체에서 공통으로 사용될 이름 변수
    String name;

    public Animal(String name) {
        this.name = name;
    }

    public void doBehavior() {
        // Dog, Eagle, Penguin 각 객체 모두 공통으로 동일한 내용으로 호출되는 메서드
        doEat();

        // 각 동물 객체별로 재구현이 필요한 이동관련 메서드
        doMove();

        // 각 동물 객체별로 재구현이 필요한 울음소리 메서드
        doCry();

        if(canFly()) { // hook method로 각 객체에서 재구현해 실행여부를 결정(optional)
            doFly();
        }
    }

    public void doEat() {
        System.out.println(name + "(은) 얌얌 밥을 먹습니다.");
    }

    public abstract void doMove();

    public abstract void doCry();

    public boolean canFly() {
        return false;
    }

    public abstract void doFly();
}

DoFly 인터페이스

: 날수 있는 알고리즘, 날수없는 알고리즘을 관리할 DoFly 인터페이스

public interface DoFly {
    // 나는 것과 관련된 메서드
    public void fly();
}

CanFly 클래스

: DoFly 인터페이스를 구현해 날 수 있는 알고리즘을 구현한 클래스로 ⇒ 알고리즘을 "캡슐화"했다.

public class CanFly implements DoFly{

    private String name;

    public CanFly(String name) {
        this.name = name;
    }

    @Override
    public void fly() {
        System.out.println("날 수 있습니다.");
    }

}

CanNotFly 클래스

: 위와 마찬가지로 날 수 없는 알고리즘을 캡슐화

public class CanNotFly implements DoFly{

    private String name;

    public CanNotFly(String name) {
        this.name = name;
    }

    @Override
    public void fly() {
        System.out.println("날개는 있지만 날  수 없습니다.");
    }

}

public class CanNotFly implements DoFly{

    private String name;

    public CanNotFly(String name) {
        this.name = name;
    }

    @Override
    public void fly() {
        System.out.println("날개는 있지만 날  수 없습니다.");
    }

}

Dog 클래스

: 강아지 객체로 Animal을 상속했으며 강아지는 날수없음으로 Animal의 canFly() hook method를 구현하지 않았다.(default false)

public class Dog extends Animal{

    public Dog(String name) {
        super(name);
    }

    @Override
    public void doMove() {
        System.out.println(name + "(은) 네발로 기어갑니다.");
    }

    @Override
    public void doCry() {
        System.out.println(name + "(은) 왈왈 짓습니다.");
    }

    @Override
    public void doFly() {
        // TODO Auto-generated method stub
    }

}

Eagle 클래스

: 아까는 doFly() 내부에 직접 상세구현했다면 이번엔 DoFly fly 를 선언해 생성자에서 CanFly 알고리즘을 주입하는 방식으로 바깠다.

펭귄의 경우 날 수 없음으로 CanNotFly가 주입될 것(즉, 알고리즘 교환이 용이하다.)

public class Eagle extends Animal {

    DoFly fly;

    public Eagle(String name) {
        super(name);
        fly = new CanFly(name); // 각 객체에서 구현한 것을 주입한다.(날 수 있는 알고리즘 객체를 넣음)
    }

    @Override
    public void doMove() {
        System.out.println(name + "(은) 날아갑니다.");
    }

    @Override
    public void doCry() {
        System.out.println(name + "(은) 쒱~ 하고 웁니다.");
    }

    @Override
    public void doFly() {
        fly.fly();
    }

    // hook method를 true로 바꿔 doFly()가 동작할 수 있도록 한다.
    @Override
    public boolean canFly() {
        return true;
    }

}

Penguin 클래스

public class Penguin extends Animal{

    DoFly fly;

    public Penguin(String name) {
        super(name);
        this.fly = new CanNotFly(name); // 펭귄은 날수 없는 알고리즘을 주입함
    }

    @Override
    public void doMove() {
        System.out.println(name + "(은) 뒤뚱뒤뚱 걷습니다.");
    }

    @Override
    public void doCry() {
        System.out.println(name + "(은) 펭귄~하고 웁니다.");
    }

    @Override
    public void doFly() {
        fly.fly();
    }

    @Override
    public boolean canFly() {
        return true;
    }

}

실행 main 클래스

public class CommonModelTest {
    private static final Logger logger = LoggerFactory.getLogger(CommonModelTest.class);


    @Test
    public void test1() {
        Dog dog = new Dog("강아지");
        dog.doBehavior();

        Eagle eagle = new Eagle("독수리");
        eagle.doBehavior();

        Penguin penguin = new Penguin("펭귄");
        penguin.doBehavior();
        /*
         *  강아지(은) 얌얌 밥을 먹습니다.
            강아지(은) 네발로 기어갑니다.
            강아지(은) 왈왈 짓습니다.
            독수리(은) 얌얌 밥을 먹습니다.
            독수리(은) 날아갑니다.
            독수리(은) 쒱~ 하고 웁니다.
            날 수 있습니다.
            펭귄(은) 얌얌 밥을 먹습니다.
            펭귄(은) 뒤뚱뒤뚱 걷습니다.
            펭귄(은) 펭귄~하고 웁니다.
            날개는 있지만 날  수 없습니다.
         * */
    }    


}

+ Recent posts