Showing posts with label Design Patterns. Show all posts
Showing posts with label Design Patterns. Show all posts

Friday, February 21, 2014

Decorator Pattern

@Desc
 : Decorator pattern은 Component의 기능을 유연하게 확장하기 위해 Sub Class를 이용하는 기법이다.
 : "Component" 란 실제로 우리가 표현하기 원하는 원래의 객체를 의미하고,
 : "Decorator" 는 그러한 Component들의 기능을 확장시켜주는 Wrapper Class로 이해할 수 있다.
 : Decorator는 Component의 Sub Class로 구현되어 Component와 다형성을 형성하기 때문에,
 : 마치 기능이 조금 추가되거나 변경된 Component 자체인 것처럼 여겨질 수 있다. 이러한 특성 때문에
 : Decorator Pattern을 이용하면 원래의 Component의 기능을 유연하게 확장할 수 있게 된다.
 : Decorator Pattern은 몇 가지 단점도 가지고 있는데,
 : 하나는 남들이 이해하기 힘들 정도로 자잘한 클래스들이 엄청나게 추가될 수 있다는 것이다.
 : 둘째는 특정 형식에 의존하는 코드에 Decorator를 적용하기엔 무리가 있다는 것이다.
 : 셋째는 Decorator들이 많기 때문에 Component를 초기화하는 데 필요한 코드가 복잡해진다는 것이다.
 : 마지막 단점은 "Factory Pattern" 이나 "Builder Pattern" 을 이용해서 보완 가능할 수 있다.
 : 요약 결론하면,
 : Decorator Pattern에서는 객체에 추가적인 요건을 동적으로 첨가한다.
 : Decorator는 Sub Class를 만드는 것을 통해서 기능을 유연하게 확장할 수 있는 방법을 제공한다.

@Example
1) Starbuzz Order System을 구현해 보고자 한다. 
2) Starbuzz에는 HouseBlend, DarkRoast, Decaf, Espresso 등의 많은 음료(Beverage)가 있고,
3) 음료에는 Milk, Soy, Mocha, Whip 등 첨가물(Condiment)이 추가될 수 있다.

4) 쉽게 생각하면 Beverage라는 Super Class를 만들고 각 음료마다 Beverage 클래스를 상속할 수 있다.
5) 그리고 각 첨가물에 따른 비용 가감은 Super Class의 Cost() 함수 내에서 처리해야 할 것이다.

6) 그런데, 이 방식은 문제가 많다.
7) 첫째로 첨가물 가격이 바뀔 때마다 Cost() 함수를 계속 수정해야만 하고,
8) 첨가물 종류가 많아지면 Cost() 함수 내부 처리가 복잡해질 수 있다.
9) 만약 새로운 음료 Ice Tea가 출시되었다면 이런 경우 Whip에 대한 처리는 없어야 한다.
10) 상속을 받았기 때문에 Cost() 함수 내용을 그대로 전달받게 되는데, 이것을 변경할 순 있지만
11) 만약 이를 변경하려면 Overriding이 필요하고 이것은 Sub Class의 복잡성을 증가시킨다.
12) 나중에는 어떤 Class가 어떤 첨가물을 추가할 수 있는지 없는지가 혼란스러울 것이다.
13) 여기서 "OCP Principle" 이 사용될 수 있다.

14) Condiment Decorator 클래스를 만들고 Component의 추상클래스인 Beverage를 상속해보자.
15) 그리고 Component 객체를 Decorator에 구성(Composition)으로 추가한다.
16) 즉, Decorator를 생성할 때 Component를 Parameter로 하여 Composition 관계를 만든다.
17) 이렇게 되면 Decorator 동작 시 Component의 동작을 변경하지 않고 기능을 추가할 수 있다.

@Principle
  " Open-Closed Principle " (OCP 원칙)
  : 클래스는 확장에 대해서는 열려 있어야 하지만,
    코드 변경에 대해서는 닫혀 있어야 한다.




@reference
  : Head First Design Patterns
    by Eric Freeman, Elisabeth Freeman, Kathy Sierra, Bert Bates, 1st Ed.

Saturday, February 15, 2014

Observer Pattern

@Desc
 : Observer pattern은 상태가 변할 때마다 객체들에게 새 소식을 알려주는 기법이다.
 : 객체 쪽에서는 계속해서 정보를 받을지 여부를 실행 중에 결정할 수 있다.
 : "한 객체의 상태가 바뀌면 그 객체에 의존하는 다른 객체들에게 연락이 가고,
    자동으로 내용이 갱신되는 방식으로 one-to-many 의존성을 정의하는 구현 방식이다."

@Example
1) Weather Monotoring Application을 구현해 보고자 한다. 
2) 이 시스템의 한 쪽에는 실제 기상 정보를 수집하는 Weather Station이 있고,
3) 다른 쪽에는 그 데이터를 수집하는 WeatherData 객체와 사용자정보를 보여주는 Display 장비가 있다.
4) 이때 Weather Station 기상 정보는 WeatherData 객체로 가져올 수 있다고 가정하자. 그러면 우리는,
5) WeatherData 객체를 사용하여 사용자에게 필요한 정보만 보여주면 된다.

6) 가장 쉬운 구현은 measurementChanged 시점에 각 Display 객체가 직접 update를 하는 방법이다.
7) 즉, WeatherData 클래스의 measurementChanged() 호출 시 xxDisplay.update() 를 수행하는 것이다.
8) 이 방식은 WeatherData 클래스가 Concrete Display 객체를 처음부터 포함하고 있어야 하며,
9) 나중에 필요가 없어져도 제거할 수 없음을 뜻한다. 
10) Display 객체가 원할 때 상태 정보를 받고, 또 원할 때 안 받을 순 없을까? 

10) 신문이나 잡지를 구독하는 방법을 생각해보자.
11) 신문사가 사업을 시작하고 매일 신문을 찍어낸다.
12) 고객이 신문사에 신문 구독을 신청하면 매일 나오는 신문을 받아볼 수 있다.
13) 신문을 더 이상 보고 싶지 않으면 구독을 해지하면 되고 그러면 더 이상 신문이 오지 않는다.
14) 신문사가 영업을 계속 하는 동안에는 누구나 원할 때 구독/해지를 할 수 있다.
15) 이 방법은 문제 해결의 좋은 방법처럼 보인다.

16) Observer Pattern에서는 Subject(신문사)와 Observer(고객)가 있다.
17) Subject는 Observer를 등록(Register)하거나, 제거(Remove)하거나, 상태변화를 공지(Notify)한다.
18) Observer는 Subject로부터 받은 정보를 갱신(Update)하고 처리한다.
19) Subject는 Observer를 잘 몰라도 된다. Observer의 공통 부분만 Interface로 제공하면,
20) Observer들 각각의 구체적인 구현과 관계없이 Subject와 Observer는 독립적으로 분리될 수 있다.
21) 이렇게 Interface로 두 객체를 느슨하게 결합하면 Observer를 원할 때 등록/제거가 가능해진다.
22) 예를 들면, Subject는 Observer Interface의 List만 가지고 있으면 되고, 
23) List 내 모든 Observer에게 상태가 변할 때마다 Notify만 해주면 된다.

@Caution
24) Notify하는 방식에는 Push Model과 Pull Model이 있다.
25) Push Model은 Subject에서 상태 정보 전체를 Observer에게 뿌리는 방식이고,
26) Pull Model은 Subject와 의존성은 생기지만 Observer가 원하는 정보만 받아갈 수 있는 방식이다.
27) Observer Pattern 구현 시에는 Observer들에게 연락을 돌리는 순서에 절대로 의존하면 안 된다.

@Principle
  " Loose Coupling " (느슨한 결합)
  : 두 객체가 느슨하게 결합되어 있다는 것은, 
    그 둘이 상호작용을 하긴 하지만 서로에 대해 잘 모른다는 것을 의미한다.



Thursday, December 12, 2013

Strategy Pattern

@Desc
 : Strategy pattern은 Class의 어떤 행위를 캡슐화하여 Algorithm군으로 분리시키는 기법이다.

@Example
1) 오리 타입에 따라 울음 소리가 달라지는 것을 구현해 보고자 한다. 예를 들면, MallardDuck과 RedheadDuck은 똑같이 "꽥꽥"거리지만, RubberDuck은 "삑삑"대고, DecoyDuck은 "..." 아무 소리도 내지 못한다. 이것을 어떻게 구현해볼까?
2) 먼저 Overriding을 생각해 보자. Duck Class에서 가장 많이 사용될 것 같은 "꽥꽥"을 기본 Class에 구현하고, "삑삑"이나 "..."을 Overriding해야 할 것이다. 그런데.. 단점이 있다. 기본 클래스의 구현인 "꽥꽥"이 아니라면 코드가 중복되어야만 한다는 것이다. "삑삑" 소리를 내는 오리가 10 종류라면 10개를 중복해서 구현해야 하는 문제가 있다. 
3) Interface를 사용하면 어떨까? 이건 제일 바보같은 아이디어다. 만약에 오리 타입이 50가지라면 50개를 전부 구현할 것인가? 공들여 전부 구현 코드를 넣었다고 치자. "꽥꽥"을 "꽤엑꽤엑"으로 바꾸려면 또 다시 50개를 수정해야 한다... 관리가 너무 힘들다.
4) 그렇다면 달라지는 부분은 어디인가? 이 부분을 찾아 달라지지 않는 부분으로부터 분리시켜 보자.
5) Duck Class의 Quack() 함수를 삭제하고 Quack() 행동을 QuackBehavior에게 위임하자.
6) QuackBehavior라는 Interface를 구현하는 Class들의 묶음을 우리는 Algorithm군으로 볼 수 있다.
7) MallardDuck과 RedheadDuck은 QuackBehavior중에서 Quack으로 행동하면 되고, RubberDuck은 Squeak으로, DecoyDuck은 MuteQuack으로 행동하면 된다.
8) 실행 중에 RubberDuck을 MuteQuack으로 변경할 필요가 생겼다면? 괜찮다. RubberDuck의 QuackBehavior만 Setter Method로 변경해주자. 간단히 행동을 바꿔줄 수 있다.
9) Strategy pattern을 이용해 중복과 변경 시 문제점을 해결할 수 있었다.

@Principle
  " Composition " (상속보다는 구성)
  : 구성을 이용하여 시스템을 만들면 유연성을 크게 향상시킬 수 있다.