스트래티지 패턴 (Strategy Pattern)

스트래티지 패턴이란.. 기능을 정의하고 변경되는 부분을 캡슐화하여 교환해서 사용하는 패턴... 말로는 어려우니 아래를 보자.

Dog라는 부모클래스를 Japan, korea dog에서 상속받고 있다.
모든 개들은 walk (걸음) 속성을 지니고 있으므로 부모 class인 Dog에 구현하고, 외형(display)은 모두 다르므로 Dog에 추상 메소드로 정의하고 자식 클래스들이 오버라이딩 하고 있다.


이 때, japan dog에 run (달리는) 기능이 필요해 부모 Dog 클래스에서 구현하고 자식 클래스들이 상속받았다.
하나 문제가 생겼다. 한국개들은 달리는 속도가 다른데 부모 클래스의 run 메소드를 그대로 상속 받는 점이였다.  korea Dog 클래스에 run 함수를 오버라이딩 하여 처리하도록 하였다.
문제는 다음과 같은 가정에서 발생한다. 만약 전 세계의 개들을 객체로 만드는 일이 필요하다. 개들마다 속도가 다르다면 하위 클래스의 run 함수 전부를 오버라이딩 할 것 인가? 가능은 하지만 이 방법은 객체지향의 장점을 잘 활용할 수 없다고 본다.

strategy 패턴은 변경되는 부분을 캡슐화하고 객체들간 교환을 통해 디자인하는 패턴이다.
strategy 패턴의 디자인 원칙을 보자면 아래와 같다.
 1. 프로그램의 변경되는 부분을 찾아내고 변경되지 않는 부분으로부터 분리한다.
 2. 상속보다는 구성을 활용한다.

위 예제로 돌아가보면, 변경되는 부분은 무엇인가? run 기능이다. 이 기능을 변하지 않는 다른 기능들과 분리시켜야 한다. 또한 상속을 배제하고 인터페이스를 통해 접근하도록 한다.
Dog 클래스는 인터페이스 변수 하나를 두는 것으로 더 이상 run에 대한 기능은 신경쓰지 않아도 된다.


위 그림을 코드로 표현해보자. 우선 인터페이스 부분은 아래와 같다.


interface DoRun {
 public void run();
}

class fast implements DoRun {
 public void run() {
  System.out.println("빠름");
 }
}

class slow implements DoRun {
 public void run() {
  System.out.println("느림");
 }
}
클래스 부분을 코드로 보면 아래와 같다.

class koreaDog extends Dog {
 public koreaDog() { dorun = new fast(); }
 
 void display() {
  System.out.println("한국개생김새");
 }
}

class japanDog extends Dog {
 public japanDog() { dorun = new slow(); }
 
 void display() {
  System.out.println("일본개생김새");
 }
}

public abstract class Dog {
 DoRun dorun;
 
 public Dog() {}
 public void walk() { 
  System.out.println("모든 개는 걸어요");
 }
 public void performRun() {
  dorun.run();
 }
 abstract void display();
 
 public static void main(String[] args) {
  Dog dog = new japanDog();
  dog.display();
  dog.performRun();
  
  dog = new koreaDog();
  dog.display();
  dog.performRun();
 }
}

정리를 해보자. 스트래티지 패턴이란, 객체마다 달라지는 부분은 오버라이딩으로 처리하기 보단, 변경되는 부분을 변경되지 않는 부분과 나누고 인터페이스를 선언해 객체간의 교환을 통해 문제를 해결하는 디자인 패턴이라 생각하면된다.

레퍼런스
- Head First 디자인패턴

댓글 없음:

댓글 쓰기