ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • 스트래티지 패턴
    Programming/Design Patterns 2010. 5. 3. 08:11

     

    디자인패턴

    1. 스트래티지 패턴(Strategy Pattern)이란?                             Strategy_Pattern.zip 예제 파일

        알고리즘 군을 정의하고 각각을 캡슐화해서 교환해서 사용할 수 있도록 만든다.
        스트래터지를 활용하면 알고리즘을 사용하는 클라이언트와는 독립적으로 알고리즘을 변경할 수 있다.

     2.'문제에 대한 인지'

         모든 것에 앞서서 현재 어떤문제가 있는지 깨닫는 과정은 정말 중요한 시간이다.

        문제가 무엇인지 알아야 솔루션을 제공할것이 아닌가?
        지금부터는 소스를 보면서 어떻게 '문제를 인지'하게 되는지 같이 살펴보자.
        상황은 다음과 같다. 아래 그림과 같이 오리클래스로 부터 상속을 받은 2개의 오리가 있다 MallaaedDuck 와 RedHeadDuck 이 그것 이다.

         1.jpg

     

    이녀석들은 오리로 부터 상속을 받았기에 당연히 울수있고(quack()) 물에뜨고(swim()) 각각의 모습이 틀리기에(한놈음 머리가 빨간색이지 않는가?)

    오버라이딩한 display() 메소드를 가진다.
    사용자는 문득 오리도 날수 있다라는 사실을 인지 하였기에 Duck클래스에 fly() 메소드를 추가 하기로 하였다. 그리고 RubberDuck(고무오리) 클래스를 추가 하였다.
    위 상황에서 상속이 가진 힘에 의해 RubberDuck 역시 울수있고(quack()) 물에뜨고(swim()) 고유의 외모(display()) 그리고 날수(fly())하게 되었다.
    고무오리는 날지 못한다. 더군다나 울수도 없고 말이다.
    이문제를 해결하기 위해 처음에 오리마다 각각의 고유의 모습을 그려내었던(display()) 방법인 오버라이딩을 해결책으로 떠올릴수 있고.
    행동 즉 메서드를 상속을 통해서 제공하므 인해 발생 하는 문제에 대해서 깨달을수 있다.(문제의 인지)

    첫번째 문제는 서브 클래스에 코드가 중복이 된다는점.
    두번째 문제는 모든 오리의 행동을 알기 힘들다는점.(어떤오리가 추가 될지 어떻게 알수 있단 말인가???)
    세번재 문제는 코드를 변경했을때 (또는 새로운 행동을 추가 했을때) 다른 오리들에게 원치 않는 영향을 끼칠수 있다는점.
    네번째 문제는 실행시점 그러니까 오리를 만들어내는 시점에 특징을 바꾸기 힘들다는 것이다.

     

    2.1 첫번째 솔루션

      우리는 경험을 통해서 배운다.  위에서 고무오리를 하나 추가해 봄으로써 앞으로 어떤일이 펼처질수 있을지 상상 할수 있었다.(어떤 오리가 튀어 나올지 모른다 )
      인터페이스란 메소드의 선언만 가지고 있고 구현은 안된것이다. 인터페이스를 이용해서 지금 문제를 일으키고 있는 행동들을 분리하자 라는 생각에 Flayble

      과  Quackable 인터페이스를 만들고 모든 오리는 이걸 구현하게 하자!

     

        2.JPG

     

    일단 보기만 해도 문제가 있어보인다. 울지 못하고 날지 못하는 1개의 오리 때문에 나머지 오리들이 전부 2개의 인터페이스를 가져다가 구현한다.

     

     2.2 두번째 솔루션

    이제 문제는 명확해 졌다. 클레스에서 변화는 부분과 변화지 않는 부분을 분리해 내는게 첫번째 이고. 전체가 아닌 일부만 날수 있거나 울수 있게 혹은 울수 없게 만들어야 한다. 이제 다형성을 사용한다.  다음을 보자.

     

         3.JPG

     

    이그림을 볼때 바로 소스가 어떻게 그려질지 머리에 그려지는 분도 있을것이다. 많은 노력과 좋은 환경 그리고 시간을 필요로 했을거라 생각이 된다. 일단 추상 클래스 Duck 부터 살펴 보면 2개의 인터페이스 형식의 레퍼런스변수(참조변수)를 선언 하고 있다. 그리고 performQuack 과 performFly 2개의 메소드를 통해서 어떤 행동을
    (꽥꽥 울어야 하는지 아니면 ?꽉 울어야 하는지 혹은 날개로 날아야 하는지 로켓으로 날아야 하는지 아니면 날수 없던지) 취해야 할지 행동을 제어하는 인터페이스에 위임 한다. 지금까지 기술한 내용을 코드로 써보면 다음과 같다.

     

    public abstract class Duck{
      Flyable    flyBehavior;
      Quackable  quackBehavior;

      public abstract void display();

      public void performQuack() {
       quackBehavior.quack()
      }

      public void performFly(){
        flyBehavior.fly();
      }

      public void setFlyBehavior(Flyable fb){
        flyBehavior = fb;
      }
     
      public void setQuackBehavior(Quackable  qb){
        quackBehavior = qb
      }
    }

    코드 자체는 전혀 어려운게 없다. 핵심은 performQuack(), performFly() 이 두개의 메소드다. 위에서 기술한 다형성의 내용을 기억 하고 있다면 이게 어떻게 쓰일지 그림이 그려질 것이다.
    setFlyBehavior()와 setQuackBehavior()의 인자인 Quackable 과 Flyable 는 아규먼트 다형성 혹은 프로모션이 라고 불리는 다형성의 형질을 이용해 값을 넘기는 아규먼트다 메인메소드 에서는 아마 다음과같이 구현될걸다

       .
       Flyable   fb = new FlyWithWings();
       Quackable qb = new Squack();

       model.setFlyBehavior(fb);    // 한줄로쓰면 다음과 같이 된다. model.setFlyBehavior(new FluWithWings());
       model.setQuackBehavior(qb);

       model.performQuack();
       model.performFly();
       .

    이렇게 만들면 부모의 메소드로 자식의 메소드를 호출하는 메서드 다형성이 발생되는 형태를 된다.
    위와 같이 위임할 객체를 선택하는 seter를 만들어 두면 실행 시점에 행동에 대한 제어가 쉬워진다. Duck 클래스를 손대지 않고 단순히 performQuack과 performFly 메소드를 호출 하는것만으로 행동되야할 객체를 선택할수 있게 된다.

    이 스트래티지 패턴이 "어떤점이 좋은지 잘 모르겠다" 면 처음에 "문제의 인지"를 할수 있었던 방법을 써보자.
    이 오리는 보통오리와 달라서 로켓 추진으로 날수 있게 되었다. 라고 해보자.
    이 오리가 가진 특성을 클래스에 추가하려면 어떻게 하면 될 것인가?

    Flyable 인터페이스를 구현한 RoketWithWings라는 클래스를 하나 추가만 하면된다.

    public class RoketWithWings implements FlyBehavior{
     public void fly(){
      System.out.println("로켓 추진으로 날아갑니다.");
     }
    }


       4.JPG 

    기능이 하나 추가 되었다고 Duck를 변경 하고 그를 상속받은 각각의 클래스를 수정 하는 이런 소모적인 일을 할필요가 전혀 없다.
    여기까지 스트래티지에 대해 예를 들어 알아보았다.

     

    • 디자인 원칙

           어플리케이션에서 달라지는 부분을 찾아내고, 달라지지 않는 부분으로 부터 분리시키는 것이 좋으며,

                구현이 아닌 인터페이스에 맞춰서 프로그래밍을 한다.

    • 상속보다는 구성을 활용한다.

                "A에는 B가 있다." 관계는 각 오리에는 FlyBehavior 와 QuackBehavior가 있으며, 각각 행동과 꽥꽥 거리는
                 행동을 위임받습니다. 두 클래스를 이런식으로 합치는 것을 구성(composition)을 이용하는 것이라고 부릅니다.
                 여기 나와있는 오리클래스에서는 행동을 상속받는 대신, 올바른 행동 객체로 구성됨으로써 행동을 부여받게 됩니다.

                 구성을 이용할시 알고리즘군을 별도의 클래스의 집합으로 캡슐화할 수 있도록 만들어주는 것 뿐 아니라, 구성요소로
                 사용하는 객체에서 올바른 행동 인터페이스를 구현하기만 하면 실행시에 행동을 바꿀 수도 있게 해 줍니다.

     

    •  디자인 패턴은 라이브러리보다 높은 단게에 속한다. 
       클래스와 객체를 구성하여 어떤 문제를 해결하는 방법을 제공하는데
       그러한 디자인을 특정 어플리케이션에 맞게 적용하는 것은 개발자들이 해야 할 일이다.
       단, 프레임워크나 라이브러리는 디자인 패턴이 아니다 개발자들이 각자의 코드에 링크시켜 쓸 수 있는
       특정 구현을 제공 할 뿐이나 라이브러리나 프레임워크를 구현하는 과정에서 디자인 패턴을 사용하는 경우는 있다.

       

    •  관리가 용이한 객체지향 시스템을 만드는 비결 가운데 하나는 "나중에 어떻게 바뀔 것인지" 에 대해 생각해 보는것이다.

     

    객체지향의 기초
      1. 추상화 - 추상클래스는 구현될 추상적인 기능만을 추상메소드로 선언하고(공통된 부분을 추려내어 일반화하기 위해) 클래스의 안의 추상메소드들은 선언부분만 있고

                      구현부분이 없는 메소드들이 있다.
      2. 캡슐화 - 함수를 통한 데이터의 접근 setter와 getter를 생각하면 된다. 세부적인 구현은 숨기고 외부에는 사용할 수 있는

                      최소의 정보만 노출하는 것이면 모두 캡슐화이다.

      3.  다형성 - 물려받은 메서드를 오버라이딩해서 자식의 상황에 맞게 정의할 때 나타나는 성질로 같은 메서드가 상속을 거치면서

                     서로 다른 기능을 수행하는 것을 의미합니다.

      4. 상속 - 부모의 필드와 메서드를 그대로 물려받는 것

     

      상속과 다형성의 예

        전자제품이라는 부모는 전원을 인가시키는 "켜기"라는 기본적인 행위가 필요합니다.

        전자제품을 상속한 TV와 냉장고는 이 "켜기"라는 행위를 갖고 있지만 실제 실행하는 방식은 차이가 있습니다.

        TV는 "전원을 꽂은 후 전원버튼을 눌러서" 켜고 냉장고는 "전원만 꽂아서" 켭니다.

        이것이 다형성이고  특히, 자바에서 다형성은 TV나 냉장고 객체를 전자제품형 변수에 담을 수 있고

        그 상태로 "켜기"를 실행하면 전자제품 클래스의 "켜기"가 아닌 각 클래스(TV, 냉장고)가 구현한 "켜기"가 실행된다는 특징을 갖고 있습니다.


    객체 지향 원칙
      1. 바뀌는 부분은 캡슐화한다.
      2. 상속보다는 구성을 활용한다.
      3. 구현이 아닌 인터페이스에 맞춰서 프로그래밍한다.

     

    'Programming > Design Patterns' 카테고리의 다른 글

    디자인 패턴 요약 정리  (0) 2010.05.03
    팩토리 패턴  (0) 2010.05.03
    옵저버 패턴  (0) 2010.05.03
    데코레이터 패턴  (0) 2010.05.03
Designed by Tistory.