본문 바로가기
CS/Design Pattern

[디자인 패턴] 전략 패턴 (Strategy Pattern)

by Nahwasa 2023. 6. 17.

스터디 메인 페이지

목차

    - ☆ 표시가 붙은 부분은 스터디 중 나온 얘기 혹은 제 개인적인 생각이나 제가 이해한 방식을 적어놓은 것으로, 책에 나오지 않는 내용입니다. 따라서 책에서 말하고자 하는 바와 다를 수 있습니다. 또한 책에는 따로 Step으로 나오지 않습니다. 설명의 편의를 위해 임의로 나눈 것 입니다.

    - 모든 이미지의 출처는 헤드퍼스트 디자인패턴 개정판(한빛미디어) 입니다.

     


     

    전략 패턴 (Strategy Pattern)

    코드 링크 : github

     

    GitHub - nahwasa/study-design-patterns: 헤드퍼스트 디자인패턴 스터디 진행하면서 각 패턴별 문제점이 있

    헤드퍼스트 디자인패턴 스터디 진행하면서 각 패턴별 문제점이 있는 코드부터 개선되는 코드까지 짜보기 위한 레포 - GitHub - nahwasa/study-design-patterns: 헤드퍼스트 디자인패턴 스터디 진행하면서

    github.com

     

     

    Step 1. 초기 코드

     

    class Duck {
        public void quack() {
            System.out.println("Quack!");
        }
    
        public void swim() {
            System.out.println("Swim!");
        }
    
        public void display() {
            System.out.println("Display!");
        }
    }
    
    class MallardDuck extends Duck {
        @Override
        public void display() {
            System.out.println("MallardDuck display!");
        }
    }
    
    class RedheadDuck extends Duck {
        @Override
        public void display() {
            System.out.println("RedheadDuck display!");
        }
    }
    
    class RubberDuck extends Duck {
        @Override
        public void quack() {
            System.out.println("Squeak!");
        }
    
        @Override
        public void display() {
            System.out.println("RubberDuck display!");
        }
    }

     

     

    Step 2. 오리가 날 수 있어야 한다!

    일부 Duck들만 fly()를 추가하려고 하는데
    Duck에 추가했더니 당연히 전부 적용되버림.
    러버덕은 날면 안되니깐 아무것도 안하게 오버라이드를 해줘야 한다.

    class Duck {
        public void quack() {
            System.out.println("Quack!");
        }
    
        public void swim() {
            System.out.println("Swim!");
        }
    
        public void display() {
            System.out.println("Display!");
        }
    
        public void fly() {
            System.out.println("Fly!");
        }
    }
    
    class MallardDuck extends Duck {
        @Override
        public void display() {
            System.out.println("MallardDuck display!");
        }
    }
    
    class RedheadDuck extends Duck {
        @Override
        public void display() {
            System.out.println("RedheadDuck display!");
        }
    }
    
    class RubberDuck extends Duck {
        @Override
        public void quack() {
            System.out.println("Squeak!");
        }
    
        @Override
        public void display() {
            System.out.println("RubberDuck display!");
        }
    
        @Override
        public void fly() {
            // do nothing
        }
    }

     

     

    Step 3. 가짜 오리 추가!

    가짜 오리(DecoyDuck)을 추가하려고 했더니
    quack, fly 둘 다 아무것도 안해야됨;
    이렇게 상속으로 처리하는건 해결책이 아닌 것 같음.
    이후에 오리가 추가될 때 마다 fly랑 quack 전부 살펴보고 상황에 따라 오버라이드 해야됨 ㅠ

     

    class Duck {
        public void quack() {
            System.out.println("Quack!");
        }
    
        public void swim() {
            System.out.println("Swim!");
        }
    
        public void display() {
            System.out.println("Display!");
        }
    
        public void fly() {
            System.out.println("Fly!");
        }
    }
    
    class MallardDuck extends Duck {
        @Override
        public void display() {
            System.out.println("MallardDuck display!");
        }
    }
    
    class RedheadDuck extends Duck {
        @Override
        public void display() {
            System.out.println("RedheadDuck display!");
        }
    }
    
    class RubberDuck extends Duck {
        @Override
        public void quack() {
            System.out.println("Squeak!");
        }
    
        @Override
        public void display() {
            System.out.println("RubberDuck display!");
        }
    
        @Override
        public void fly() {
            // do nothing
        }
    }
    
    class DecoyDuck extends Duck {
        @Override
        public void display() {
            System.out.println("DecoyDuck display!");
        }
    
        @Override
        public void quack() {
            // do nothing
        }
    
        @Override
        public void fly() {
            // do nothing
        }
    }

     

     

    Step 4. 상속대신 인터페이스?

    상속은 아닌 것 같으니 인터페이스를 추가해서 해결해보려 하는 과정임.
    문제는 기존에 상속해서 fly랑 quack 잘 쓰고 있던 오리들도
    구조가 바뀌면서 전부 fly랑 quack을 구현해야 됨;
    따라서 전체적인 오리들을 전부 손대야 하는대다가 코드중복이 발생해버림.
    이것도 아닌 것 같음.

     

    class Duck {
            public void swim() {
                System.out.println("Swim!");
            }
    
            public void display() {
                System.out.println("Display!");
            }
        }
    
        interface Flyable {
            public void fly();
        }
    
        interface Quackable {
            public void quack();
        }
    
        class MallardDuck extends Duck implements Flyable, Quackable {
            @Override
            public void display() {
                System.out.println("MallardDuck display!");
            }
    
            @Override
            public void fly() {
                System.out.println("Fly!");
            }
    
            @Override
            public void quack() {
                System.out.println("Quack!");
            }
        }
    
        class RedheadDuck extends Duck implements Flyable, Quackable {
            @Override
            public void display() {
                System.out.println("RedheadDuck display!");
            }
    
            @Override
            public void fly() {
                System.out.println("Fly!");
            }
    
            @Override
            public void quack() {
                System.out.println("Quack!");
            }
        }
    
        class RubberDuck extends Duck implements Quackable {
            @Override
            public void quack() {
                System.out.println("Squeak!");
            }
    
            @Override
            public void display() {
                System.out.println("RubberDuck display!");
            }
        }
    
        class DecoyDuck extends Duck {
            @Override
            public void display() {
                System.out.println("DecoyDuck display!");
            }
        }

     

     

    Step 5. 전략 패턴 적용! 바뀌는 부분과 바뀌지 않는 부분 분리.

    변화하는 부분을 뽑아내서 별도의 클래스 집합으로 분리!

     

    public static void main(String[] args) {
        new Step5().simulate();
    }
    
    public void simulate() {
        Duck mallard = new MallardDuck();
        mallard.performQuack();
        mallard.performFly();
    }
    
    abstract class Duck {
        FlyBehavior flyBehavior;
        QuackBehavior quackBehavior;
    
        public Duck() {}
    
        public abstract void display();
    
        public void performFly() {
            flyBehavior.fly();
        }
    
        public void performQuack() {
            quackBehavior.quack();
        }
    
        public void swim() {
            System.out.println("모든 오리는 물에 뜹니다. 가짜 오리도 뜨죠.");
        }
    }
    
    interface FlyBehavior {
        public void fly();
    }
    
    class FlyWithWings implements FlyBehavior {
        @Override
        public void fly() {
            System.out.println("날고 있어요!~");
        }
    }
    
    class FlyNoWay implements FlyBehavior {
        @Override
        public void fly() {
            System.out.println("저는 못 날아요");
        }
    }
    
    interface QuackBehavior {
        public void quack();
    }
    
    class Quack implements QuackBehavior {
        @Override
        public void quack() {
            System.out.println("꽥");
        }
    }
    
    class MuteQuack implements QuackBehavior {
        @Override
        public void quack() {
            System.out.println("<< 조용~ >>");
        }
    }
    
    class Squeak implements QuackBehavior {
        @Override
        public void quack() {
            System.out.println("삑");
        }
    }
    
    class MallardDuck extends Duck {
        public MallardDuck() {
            quackBehavior = new Quack();
            flyBehavior = new FlyWithWings();
        }
    
        @Override
        public void display() {
            System.out.println("저는 물오리입니다");
        }
    }

     

     

    Step 6. 전략 패턴 적용! 동적으로 행동 지정 가능하게 하기

    세터 메소드로 오리의 행동을 동적으로 바꿀 수 있도록 함.
    이제 중간에 오리의 행동 변경 가능.

    ☆ 물론 동적으로 변경 가능하다는 점이 유효한지는 프로젝트마다 달라질 것이라 생각됨. 항상 좋은건 절대 아님.

     

    public static void main(String[] args) {
        new Step6().simulate();
    }
    
    public void simulate() {
        Duck mallard = new MallardDuck();
        mallard.performQuack();
        mallard.performFly();
    
        Duck model = new ModelDuck();
        model.performFly();
        model.setFlyBehavior(new FlyRocketPowered());
        model.performFly();
    }
    
    abstract class Duck {
        FlyBehavior flyBehavior;
        QuackBehavior quackBehavior;
    
        public Duck() {}
    
        public abstract void display();
    
        public void performFly() {
            flyBehavior.fly();
        }
    
        public void performQuack() {
            quackBehavior.quack();
        }
    
        public void swim() {
            System.out.println("모든 오리는 물에 뜹니다. 가짜 오리도 뜨죠.");
        }
    
        public void setFlyBehavior(FlyBehavior fb) {
            flyBehavior = fb;
        }
    
        public void setQuackBehavior(QuackBehavior qb) {
            quackBehavior = qb;
        }
    }
    
    interface FlyBehavior {
        public void fly();
    }
    
    class FlyWithWings implements FlyBehavior {
        @Override
        public void fly() {
            System.out.println("날고 있어요!~");
        }
    }
    
    class FlyNoWay implements FlyBehavior {
        @Override
        public void fly() {
            System.out.println("저는 못 날아요");
        }
    }
    
    class FlyRocketPowered implements FlyBehavior {
        @Override
        public void fly() {
            System.out.println("로켓 추진으로 날아갑니다!");
        }
    }
    
    interface QuackBehavior {
        public void quack();
    }
    
    class Quack implements QuackBehavior {
        @Override
        public void quack() {
            System.out.println("꽥");
        }
    }
    
    class MuteQuack implements QuackBehavior {
        @Override
        public void quack() {
            System.out.println("<< 조용~ >>");
        }
    }
    
    class Squeak implements QuackBehavior {
        @Override
        public void quack() {
            System.out.println("삑");
        }
    }
    
    class MallardDuck extends Duck {
        public MallardDuck() {
            quackBehavior = new Quack();
            flyBehavior = new FlyWithWings();
        }
    
        @Override
        public void display() {
            System.out.println("저는 물오리입니다");
        }
    }
    
    class ModelDuck extends Duck {
        public ModelDuck() {
            flyBehavior = new FlyNoWay();
            quackBehavior = new Quack();
        }
    
        @Override
        public void display() {
            System.out.println("저는 모형 오리입니다");
        }
    }

     

     

    전략 패턴?

    • 알고리즘군을 정의하고 캡슐화해서 각각의 알고리즘군을 수정해서 쓸 수 있게 해준다.
    • 클라이언트로부터 알고리즘을 분리해서 독립적으로 변경할 수 있음!
    • 즉, 객체가 할 수 있는 알고리즘(행위) 각각의 전략 클래스들을 만들어서 갈아끼울 수 있게 해줌.

     

    최종 코드 : github

    댓글