[1장] Strategy Pattern

ss0510s·2022년 7월 10일
0

Strategy Pattern

알고리즘군을 정의하고 캡슐화해서 각각의 알고리즘군을 수정해서 쓸 수 있게 하는 패턴

  • 객체의 행위를 바꾸는 경우, 직접 수정하지 않고 전략이라고 부르는 캡슐화한 알고리즘을 컨텍스트(상황, 문맥, 맥락 등 어떠한 작업을 완료하는 데 필요한 모든 관련 정보) 안에서 바꿔주면서 상호 교체가 가능하게 만드는 패턴이다.

  • 컨텍스트(변하지 않는 부분)에서 전략(변하는 부분)을 별도로 분리한다.

  • 클라이언트로부터 알고리즘을 분리해서 독립적으로 변경이 가능하다

  • 클라이언트가 Context에게 Strategy를 '주입(DI:Dependancy Injection)'한다.

OO 기초

  • 추상화: 불필요한 정보를 생략하고 중요한 것에 중점을 두어 모델링하는 것
  • 캡슐화: 외부에서의 접근을 제한하기 위해 인터페이스를 제외한 세부 정보를 은닉하는것
  • 다형성: 하나의 메시지에 대해 각각의 객체가 가지고 있는 고유한 방법으로 응답할 수 있는 능력
  • 상속: 상위클래스의 모든 속성과 연산을 하위클래스가 물려받는 것

디자인 원칙

  • 변하는 부분과 변하지 않는 부분을 구분하고, 변하는 부분을 캡슐화한다.
  • 상속보다는 구성을 활용한다.
  • 구현보다는 인터페이스에 맞춰서 프로그래밍 한다.

오리 시뮬레이션 게임

  • 모든 오리가 소리를 내고 수영을 한다 -> 오리(Duck)이라는 슈퍼클래스를 작성
  • 모든 오리의 모양이 다르다 -> display() 메서드를 추상 메서드로 생성

문제: 오리 시뮬레이션 게임에 몇몇 오리에게 fly() 행위 추가

방안

(1) 상속

  • 메소드를 오버라이드하여 객체마다 다른 행위를 부여한 경우, 서브클래스에서 코드가 중복되고 모든 행위를 알기 어렵다. 또한, 실행시 수정이 힘들고 다른 인스턴스에 원치 않게 영향을 미칠 수 있다.

(2) fly 행위를 인터페이스로 정의

  • 코드를 재사용하지 못하므로 코드 중복 문제가 발생하고, 날아다니는 모든 클래스를 수정해야 한다.

(3) 행위를 인터페이스로 정의하고, 구체적인 행위를 구현하는 클래스 생성

  • 재사용이 가능하고, 영향을 끼치지 않고 새로운 행위 추가가 가능하다.
  • 실행시간 동안 변경이 가능하다.

코드 구현

  1. 오리에게서 변하는 부분을 분리한다.
  • Quack(), Fly()을 캡슐화하고 인터페이스로 정의한다.
  • 인터페이스에 대한 구체적인 행동을 구현하는 클래스를 정의한다.
  • Fly() - FlyWithWings, FlyNoWay
  1. 추상클래스 Duck과 MalladDuck, RedHeadDuck 등 Duck을 상속 받는 구상 클래스들이 존재한다.
  • Duck에게는 Fly와 Quack 행동이 있으며, 각각 행동을 위임받는다. -> composition 관계

Duck.java : 추상 클래스

public abstract class Duck {
	// 행동 인터페이스 형식의 레퍼런스 변수 선언-> composition 관계
    FlyBehavior flyBehavior;
    QuackBehavior quackBehavior;
    
    public Duck() { }
    public abstract void display(); // 추상 메서드
    
    // 행위를 수행할 함수 선언 - Duck에서는 구체적인 행위를 구현할 필요가 없다.
    public void performFly() {
        flyBehavior.fly(); // 행위를 행동 클래스에 위임
    }
    public void performQuack() {
        quackBehavior.quack(); // 행위를 행동 클래스에 위임
    }
    // 동적으로 행위 지정을 위한 함수
    public void SetFlyBehavior(FlyBehavior fb) {
        flyBehavior = fb;
    }
    public void SetQuackBehavior(QuackBehavior qb) {
        quackBehavior = qb;
    }
    public void swim() {
        System.out.println("All ducks float, even decoys!");
    }
}

Fly, Quack 인터페이스

  • 행위를 정의하고 행위 구현 클래스에서 실제 행위를 구현한다. 행위를 계속 추가할 수 있다. -> 다형성
public interface FlyBehavior {
    public void fly();
}
public class FlyWithWings implements FlyBehavior {
    public void fly() { // 구체적인 행위
        System.out.println("FlyWithWings");
    }
}
public interface QuackBehavior {
    public void quack();
}
public class Quack implements QuackBehavior {
    public void quack() { // 구체적인 행위 지정
        System.out.println("quack");
    }
}

MalladDuck.java

  • 구상 클래스를 정의한다. Duck에게 인스턴스 변수를 상속받아 객체 생성시 행위를 지정한다.
public class MallardDuck extends Duck{
    public MallardDuck() {
    // 행위 선언
        quackBehavior = new Quack();
        flyBehavior = new FlyWithWings();
    }
    public void display() {
        System.out.println("Mallard duck");
    }
}

TestDuck.java

public class TestDuck {
    public static void main(String[] args) {
        MallardDuck m1 = new MallardDuck();
        // fly, quack 행위 수행
        m1.display();
		m1.performQuack();
        m1.performFly();
        // 동적으로 행위 지정 - 실행시간 동안 행위를 지정할 수 있다.
        m1.setFlyBehavior(new FlyNoWay());
        
        m1.performFly();
    }
}

장단점

장점

  • Context가 Strategy에 의존하고 있기 때문에 Context 코드의 변경없이 전략을 추가할 수 있다. => OCP를 만족한다.

단점

  • 클래스가 많이 늘어나면 복잡도가 증가한다.
  • 클라이언트가 전략에 대해 알고 있어야 한다.

적용

passport 라이브러리

  • Node.js에서 인증모듈을 구현할 때 쓰는 미들웨어 라이브러리
  • LocalStrategy, Oauth 전략 등 여러 전략을 기반으로 인증이 가능하다.
profile
개발자가 되기 위해 성장하는 중입니다.

0개의 댓글