ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [Design Pattern] 전략 패턴 (Strategy Pattern)
    공부/디자인 패턴 2021. 7. 24. 20:52

    전략 패턴이란 각각의 알고리즘을 교환이 가능하도록 정의, 캡슐화를 한 다음, 서로 교환해서 사용할 수 있는 패턴입니다. 즉, 객체들이 할 수 있는 동작을 각각의 전략으로 만들어 놓고 동적으로 동작을 변경해야 한다면 전략만 변경하여 동작이 바뀌도록 하는 패턴입니다.

    이러한 패턴을 사용하면 OCP에 위배되지 않고 시스템이 거대해졌을 때, 메소드의 중복을 해결할 수 있습니다.

    클래스 다이어그램

    Context: Strategy의 메소드를 호출해서 사용하는 클래스

    Strategy: 전략을 사용하기 위한 인터페이스

    ConcreteStrategyA, B, C: Strategy 인터페이스를 실제 구현하는 클래스

    예시

    먼저 아래와 같이 사람과 오리를 나타내는 인터페이스와 클래스가 있다고 합니다. cry 메소드를 호출하면 오리는 꽉이라고 울고 사람은 울지 않습니다.

    public interface Sound {
        void cry();
    }
    
    public class Duck implements Sound {
        @Override
        public void cry() {
            System.out.println("꽉");
        }
    }
    
    public class Person implements Sound {
        @Override
        public void cry() {
            System.out.println("울지 않아");
        }
    }
    
    Quacker duck = new Duck();
    Quacker person = new Person();
    
    duck.cry();
    person.cry();

    시간이 지나서 사람도 오리 울음을 내게 되어 울음소리를 변경한다면 이미 개발되어 있는 메소드를 수정해야 합니다. 이는 OCP에 위배되며 시스템이 거대하다면 유지보수를 어렵게 만듭니다.

    이러한 상황을 전략 패턴을 사용해 해결하면 다음과 같습니다.

    우는 방식과 울지 않는 방식 두 가지가 있으니 두 방식에 대한 전략 클래스를 생성합니다. 그 다음, cry 메소드를 정의하고 이를 캡슐화 하기 위해 전략 인터페이스를 생성합니다.

    public interface SoundStrategy {
        void cry();
    }
    
    public class CryStrategy implements SoundStrategy {
        @Override
        public void cry() {
            System.out.println("꽉");
        }
    }
    
    public class NoCryStrategy implements SoundStrategy {
        @Override
        public void cry() {
            System.out.println("울지않아");
        }
    }

    다음은 울음을 내는 동물을 정의합니다.

    오리, 사람과 같은 동물은 cry 메소드를 통해 울 수 있습니다. 각 클래스에서 직접 cry 메소드를 구현하지 않고 어떻게 울 것인지에 대한 전략을 정하고 해당 전략을 사용하여 울도록 정의하고 사람과 오리 클래스도 구현합니다.

    public class CryingContext {
        private SoundStrategy soundStrategy;
    
        public void cry() {
            soundStrategy.cry();
        }
    
        public void setSoundStrategy(SoundStrategy soundStrategy) {
            this.soundStrategy = soundStrategy;
        }
    }
    
    public class Person extends CryingContext {
        public Person() {
            System.out.println("나는 사람");
        }
    }
    
    public class Duck extends CryingContext {
        public Duck() {
            System.out.println("나는 오리");
        }
    }

    이렇게 정의한 다음, 아래와 같이 진행하면 위에서 발생한 OCP 위배 문제를 해결할 수 있습니다.

    CryingContext person = new Person();
    CryingContext duck = new Duck();
    // 나는 사람
    // 나는 오리
    
    person.setSoundStrategy(new NoCryStrategy());
    duck.setSoundStrategy(new CryStrategy());
    
    person.cry();
    duck.cry();
    // 울지않아
    // 꽉
    
    // 사람이 드디어 오리처럼 울 수 있음
    person.setSoundStrategy(new CryStrategy());
    person.cry();
    // 꽉

    파이썬

    from abc import abstractmethod, ABCMeta
    
    class SoundStrategy(metaclass=ABCMeta):
        @abstractmethod
        def cry(self):
            pass
    
    class CryStrategy(SoundStrategy):
        def cry(self):
            print('꽥')
    
    class NoCryStrategy(SoundStrategy):
        def cry(self):
            print('울지 않아')
    
    class CryingContext:
        def __init__(self):
            self.__sound_strategy = None
    
        def cry(self):
            self.__sound_strategy.cry()
    
        @property
        def sound_strategy(self):
            return self.__sound_strategy
    
        @sound_strategy.setter
        def sound_strategy(self, strategy):
            self.__sound_strategy = strategy
    
    class Duck(CryingContext):
        def __init__(self):
            super().__init__()
            print('나는 오리')
    
    class Person(CryingContext):
        def __init__(self):
            super().__init__()
            print('나는 사람')
    
    person = Person()
    duck = Duck()
    # 나는 사람
    # 나는 오리
    
    person.sound_strategy = NoCryStrategy()
    duck.sound_strategy = CryStrategy()
    
    person.cry()
    duck.cry()
    # 울지 않아
    # 꽥
    
    person.sound_strategy = CryStrategy()
    person.cry()
    # 꽥

    댓글