ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • 전략 패턴 (Strategy Pattern)
    디자인 패턴 2022. 8. 13. 15:13

    전략 패턴


    여러 알고리즘들을 캡슐화하고 상호 교환 가능하게 만드는 패턴

    • 컨텍스트에서 사용할 알고리즘들을 클라이언트에서 선택한다.

     

    즉, 특정 업무를 수행하는 방법이 여러가지 일 경우 여러가지 방법들을 각각의 클래스로 캡슐화하면 캡슐화된 것을 공통 인터페이스로 추상화하여 업무를 수행하는 Client 에서는 공통 인터페이스를 사용하게 하여 Client 코드는 변경되지 않고도 여러 방법들을 바꿔 낄 수 있다.

     

     

    • Context : 원래 로직을 수행하던 클래스
    • ConcreteStrategy : 각각의 알고리즘들이 구현된 클래스
    • Strategy : 추상화된 알고리즘 수행 인터페이스

     

     

     

    전략 패턴 적용 전


    "무궁화 꽃이 피었습니다" 를 실행하는 프로그램이 있다.

     

    이 게임은 "무궁화 꽃이" 까지는 파란불이며, "피었습니다." 까지는 빨간불로 구현을 하였고 여기에 Speed 라는 필드를 정의하여 "무궁화 꽃이" 혹은 "피었습니다." 를 말하는 속도를 조절 할 수 있도록 하는 기능을 넣었다.

     

     

    BlueLightRedLight

    public class BlueLightRedLight {
      private int speed;
    
      public BlueLightRedLight(int speed) {
        this.speed = speed;
      }
      
      public void blueLight() {
        if (speed == 1) {
          System.out.println("무 궁 화 꽃 이");
        } else if (speed == 2) {
          System.out.println("무궁화 꽃이");
        } else if (speed == 3){
          System.out.println("무광꼬치");
        }
      }
      
      public void redLight() {
        if (speed == 1) {
          System.out.println("피 었 습 니 다.");
        } else if (speed == 2) {
          System.out.println("피었습니다.");
        } else if (speed == 3){
          System.out.println("펴씀다");
        }
      }
    }

     

     

    문제점

    speed 에 따라 분기를 나눠야하고 다르게 행동해야 한다. 

    또한 다르게 행동하기 위해 Client 에서 Speed 를 1, 2, 3 과 같은 구체적인 숫자를 바꿔줘야하고 새로운 Speed 4 가 생기면 기존 코드를 수정해야 하는 문제가 발생한다.

     

    이를 전략 패턴을 이용하여 해결해보자.

     

     

     

    전략 패턴 적용 후


     

    먼저 Strategy 인터페이스를 정의한다.

    // Strategy
    public interface Speed {
      void blueLight();
      void redLight();
    }

     

     

    ConcreteStrategy 를 구현하자.

    Slow, Normal, Faster 를 정의하여 이전에 방식의 speed 1, 2, 3 에 해당되는 기능을 구현해준다.

    public class Slower implements Speed {
      @Override
      public void blueLight() {
        System.out.println("무 궁 화 꽃 이");
      }
    
      @Override
      public void redLight() {
        System.out.println("피 었 습 니 다.");
      }
    }
    public class Normal implements Speed {
      @Override
      public void blueLight() {
        System.out.println("무궁화꽃이");
      }
    
      @Override
      public void redLight() {
        System.out.println("피었습니다.");
      }
    }
    public class Faster implements Speed {
      @Override
      public void blueLight() {
        System.out.println("무광꼬치");
      }
    
      @Override
      public void redLight() {
        System.out.println("펴씀다.");
      }

     

     

    Context 인 BlueLightRedLight 는 아래와 같다.

    // Context
    public class BlueLigthRedLight {
      private Speed speed;
    
      public BlueLigthRedLight(Speed speed) {
        this.speed = speed;
      }
      
      public void blueLight() {
        speed.blueLight();
      }
      
      public void redLight() {
        speed.redLight();
      }
    }

    생성자에서 Speed 타입 클래스 (Slower, Normal, Faster) 중 하나를 받아 그에 따르는 로직을 처리한다.

     

     

    Client 는 아래와 같이 호출 할 수 있다.

    public class Client {
      public static void main(String[] args) {
        BlueLigthRedLight blueLigthRedLight = new BlueLigthRedLight(new Slower());
        blueLigthRedLight.blueLight();
        blueLigthRedLight.redLight();
      }
    }
    

     

     

    만약, BlueLight 와 RedLight 의 속도를 다르게 하고 싶으면 생성자가 아닌 메소드에서 Speed 타입 클래스를 인자로 받으면 된다.

    public class BlueLigthRedLight {
    
      public void blueLight(Speed speed) {
        speed.blueLight();
      }
    
      public void redLight(Speed speed) {
        speed.redLight();
      }
    }
    public class Client {
      public static void main(String[] args) {
        BlueLigthRedLight blueLigthRedLight = new BlueLigthRedLight();
        blueLigthRedLight.blueLight(new Slower());
        blueLigthRedLight.redLight(new Faster());
      }
    }

     

     

     

    장점과 단점


    장점

    • 새로운 전략을 추가하더라도 기존 코드를 변경하지 않는다. (OCP)
    • 상속 대신 위임을 사용할 수 있다.
    • 런타임에 전략을 변경할 수 있다.

     

    단점

    • 복잡도가 증가한다.
    • 클라이언트 코드가 구체적인 전략을 알아야 한다.

     

     

     

    실무에선 어떻게 쓰이나?


    자바의 Comparator

    public class StrategyInJava {
      public static void main(String[] args) {
        List<Integer> numbers = new ArrayList<>();
        numbers.add(2);
        numbers.add(1);
        numbers.add(3);
        numbers.add(5);
        numbers.add(4);
    
        Collections.sort(numbers, new Comparator<Integer>() {
          @Override // ConcreteStrategy
          public int compare(Integer o1, Integer o2) {
            return o1 - o2;
          }
        });
      }
    }

     

     

     

    Spring 의 ApplicationContext 등등

    public class StrategyInSpring {
      public static void main(String[] args) {
        ApplicationContext applicationContext = new ClassPathXmlApplicationContext();
        ApplicationContext applicationContext1 = new FileSystemXmlApplicationContext();
        ApplicationContext applicationContext2 = new AnnotationConfigApplicationContext();
    
        BeanDefinitionParser parser;
    
        CacheManager cacheManager; // 다양한 캐시 전략을 제공 
      }
    }

     

     

     

    참고


     
Designed by Tistory.