Gsong's Blog

State pattern


프로그램의 상태에 따라 처리할 게 다르다면 어떻게 처리해야 될까요? 제일 먼저 생각나는 방법은 분기문이네요. if, else 를 써서 각 state 에 따라 수행할 코드들을 쓰는 겁니다. 그런데 이렇게 하게 되면 시간이 지나면서 분기문이 소스코드 곳곳에 생기게 됩니다.

하나 하나 살펴보도록 하지요. 먼저 Context.Request() 라는 함수에서 콜되는 부분을 따라가면 아래처럼 분기문이 있었을 겁니다.

class Context { STATECODE State; Request() { Handle(); } Handle() { if (State == Initiating) { // Do some initialization works } else if (State == Running) { // Do some works for Running mode } else if (State == Idling) { // Do some works for Idling mode } } }

분기문을 쓰는 함수가 Request() 하나 였다면 크게 문제가 없을 겁니다만, 보통은 코딩하다 보면 아래처럼 여러개가 생기곤 합니다.

class Context { STATECODE State; Request() { Handle(); } Handle() { if (State == Initiating) { // Do some initialization works } else if (State == Running) { // Do some works for Running mode } else if (State == Idling) { // Do some works for Idling mode } } GetValue() { if (State == Initiating) return 0; else if (State == Running) return 1; else if (State == Idling) return 2; } }

좀 단순하게 표현한 것이지만, 위의 GetValue() 함수 처럼 if-else 구문을 포함하는 게 늘어날 수 있다는 의미입니다. 이걸 없애기 위해서 State pattern 을 도입합니다. State pattern 은 이 if-else 구문을 상속을 통해 애초에 없애는 패턴입니다.State_Design_Pattern_UML_Class_Diagram

State pattern 을 적용해서 Context 클래스를 바꿔보면 다음처럼 됩니다.

class Context { State * pState; Request() { pState->Handle(); } GetValue() { return pState->GetValue(); } }

분기문이 보이지 않네요. State 클래스들도 추가해 보지요.

class State { Handle(); GetValue(); }; class InitiatingState : State { Handle() { // Do some initiating works } GetValue() { return 0; } }; class RunningState : State { Handle() { // Do some works for running mode } GetValue() { return 1; } }; class IdlingState : State { Handle() { // Do some works for Idling mode } GetValue() { return 2; } };

이로써 if-else 구문은 없어지고 if else 의 각 분기에 해당하는 코드들은 State 클래스를 상속받아 구현한 ConcreteState Class 들 즉, InitiatingState, RunningState, IdlingState 로 나눠졌습니다. 물론 여기서 Context 에 있는 State 포인터 값을 바꿔주는 코드가 따로 필요 합니다.

void main() { Context ct; ct.pState = new InitiatingState(); ct.Request(); ct.pState = new RunningState(); print ct.GetValue(); ct.pState = new IdlingState(); ct.Request(); }

Context 를 쓰는 클라이언트 코드는 위처럼 되겠지요. 이렇게 하면 새로운 State 가 추가되거나 기존의 State 에 따른 코드가 바뀌어야 할 때, 소스코드 전체에 퍼져 있는 if-else 를 찾아 바꿔줘야 하는 수고를 덜 수 있습니다. 해당 ConcreteState 코드만 바꿔주거나 추가하면 충분합니다.

하지만, 위 코드도 약간 개선할 점들이 있습니다. 보시다시피 State 가 바뀔때마다 새로운 ConcreteState 인스턴스를 만들어 할당해줘야 되기 때문에, 불필요한 메모리 할당이 자주 일어날 수 있습니다. 이럴 경우 Singleton pattern 이나 Flyweight pattern 을 활용해 보면 되겠지요.

http://en.wikipedia.org/wiki/State_pattern

크리에이티브 커먼즈 라이센스
Creative Commons License
2008/08/10 13:41 2008/08/10 13:41
top

미투데이로 한마디트위터로 한마디페이스북에 한마디
Dev : 2008/08/10 13:41 Trackback. : Comment.

TRACKBACK ADDRESS :: http://www.gsong.pe.kr/tc/trackback/403

 

Facade pattern


fa·cade, ->ade, 〕〔Fn. (건물의) 정면(front), (접해 있는) 면;겉보기, 외관, 허울

Facade 패턴은 서브시스템을 블랙박스 형태로 다루고자 할 때 유용합니다. 단어의 뜻에서도 알 수 있듯이 껍데기만 알고 그 속에 대해서는 알고 싶지 않을 때 씁니다. 객체지향에서 쓰이는 encapsulation 하고 비슷합니다. 다만 encapsulation 은 연산들을 숨기는 데 반해 Facade 패턴은 여러 클래스들을 포함한 서브시스템을 숨긴다는 것이 다릅니다.

Image:FacadeDesignPattern.png 

위 그림에서도 볼 수 있듯이 하위 패키지들을 다루는 일을 Facade 클래스가 담당하고 Client 클래스에서는 Facade 의 인터페이스만을 이용합니다. Client 코드가 간결해지는 효과가 있을 수 있겠지요.

펀드매니저를 한번 생각해 볼까요? 우리는 펀드매니저가 주식에 투자를 하는 지 채권에 투자를 하는 지 아니면 카지노에 가서 돈을 불려 오는 지 모릅니다. 다만 펀드매니저에게 재테크() 라는 명령을 할 뿐이지요. 여기서 펀드매니저 클래스가 바로 위의 Facade 클래스가 되고 주식, 채권, 도박 등이 위 그림의 Class1, Class2, Class3 에 해당합니다.

Facade 패턴은 굳이 이름을 붙여 부르지 않아도 누구나 이미 만들어 본 경험이 있을 겁니다. 경계를 나누고 정리하고자 하는 욕구는 자연스러운 것이니까요. 참 익숙하고 쉬운 패턴이지만, 딱 하나 발음만 주의하시면 됩니다. 퍼사드 패턴 입니다.

크리에이티브 커먼즈 라이센스
Creative Commons License
2007/09/20 00:08 2007/09/20 00:08
top

미투데이로 한마디트위터로 한마디페이스북에 한마디
Dev : 2007/09/20 00:08 Trackback. : Comment.

TRACKBACK ADDRESS :: http://www.gsong.pe.kr/tc/trackback/167

 

Prototype Pattern


Abstract Factory Pattern 에 이은 Prototype Pattern 입니다. Abstract Factory 에서 마지막에 나왔던 단점 중 하나가 각 Factory 들을 적절히 관리해야만 한다는 것이었지요. Prototype 은 그 부분을 조금 더 개선한 패턴입니다.

Factory 들이 여러개 있을 경우, 어떤 조건에 따라 어떤 Factory 를 쓸 것인가하는 것을 결정해 주는 분기문이 객체 생성 전 어딘가에 반드시 있어야 합니다. 예를 들어 Abstrac Factory Pattern 글의 마지막에 나왔던 pseudo code 를 다시 한번 보지요.
[CODE]
// Application code  
FruitStore  store;
if ( today == Mon || today == Tue || today == Wed)
    store =  new GuyFruitStore();
else
    store = new AzummaFruitStore();
Fruit  f  = store.TakeMoneyAndGive( MySelf );
[/CODE]
Factory 를 통해 Fruit 의 인스턴스를 생성하기 전에 Factory 의 종류를 결정할 분기문이 필요한 것을 알 수 있습니다. Prototype 패턴은 이런 경우 유용합니다. 먼저 아래의 다이어그램을 한번 보지요.

Prototype pattern

from http://en.wikipedia.org/wiki/Image:Prototype_classdia.png under GPL

위 그림에서 Clone() 이라는 메소드가 보이지요? 그게 Prototype 패턴의 핵심입니다. 분기문을 쓰지 않고 Clone() 이라는 메소드를 상속 받게 해서 if-else 나 switch 같은 문을 대신하겠다는 것이지요. 약간 억지스럽겠지만 위에 나왔던 코드를 Prototype 으로 만들어 볼까요?

일단 과일 상점 클래스에 Clone() 이라는 메소드를 둡시다.
[CODE]
abstract class FruitStore
{
    abstract Fruit   TakeMoneyAndGive(Guest g);
    abstract Fruit Clone();
}
[/CODE]
그리고 FruitStore 의 상속을 받은 클래스들은 Clone() 를 구현해야겠지요. 자 그럼 이 메소드를 추가함으로써 App code 가 어떻게 바뀌는 지 한번 보지요.
[CODE]
// Application code
FruitStoreCollection storePerWeek;

Initialize()
{
    GuyFruitStore guyStore = new GuyFruitStore();
    AzummaFruitStore azStore = new AzummaFruitStore();
    BadFruitStore badStore = new BadFruitStore();

    storePerWeek['Mon'] = guyStore ;
    storePerWeek['Tue'] = guyStore ;
    storePerWeek['Wed'] = guyStore ;
    storePerWeek['Thu'] = azStore ;
    storePerWeek['Fri'] = azStore;
    storePerWeek['Sat'] = badStore;
    storePerWeek['Sun'] = badStore;
}

main()
{
    Initialize();

    FruitStore store =
        storePerWeek[Today.ToString()].Clone();

    Fruit f = store.TakeMoneyAndGive(MySelf);
}
[/CODE]
초기화 해주는 Initialize() 함수 때문에 전체 코드는 좀 길어진 것 같습니다만, 핵심은 main() 함수를 보시면 됩니다. Fruit 객체를 얻기 위해 Factory 를 가져와야 되는데, Abstract Factory 에서는 Factory 의 구체적인 타입을 if 문 등을 통해 결정했어야 되는 반면 Prototype 패턴에서는 Clone() 이라는 메소드를 사용하므로 if 문이 필요 없게 됩니다.

위의 예로 든 코드는 앞서의 이야기에서 이어오기 위해 과일상점 얘기를 그대로 가져오다 보니 좀 지저분합니다. Prototype pattern 의 핵심은 Clone() 메소드를 이용하여 객체를 생성해낸다는 것임을 기억하면 될 것 같습니다. 예를 들어 객체를 처음부터 다시 만드는 비용이 너무 큰 경우 Clone() 메소드를 이용해서 기존의 객체를 약간 바꿔서 재사용하는 식으로 쓸 수 있을 겁니다.[footnote]http://compstat.chonbuk.ac.kr/rightway/designpatterns/prototype.html 여기를 보시면 prototype pattern 의 주된 사용 목적이 복잡한 객체를 만들어내기 위한 것이라고 하네요. 아무래도 예로 들은 코드가 적절한 예가 아닌가 봅니다. 조만간에 다른 걸로 바꿔봐야 겠네요.[/footnote]
크리에이티브 커먼즈 라이센스
Creative Commons License
2007/05/16 23:33 2007/05/16 23:33
top

미투데이로 한마디트위터로 한마디페이스북에 한마디

TRACKBACK ADDRESS :: http://www.gsong.pe.kr/tc/trackback/115

  1. Tracked from mcsong's languid afternoon 2007/08/28 21:59 DELETE

    Subject: Prototype Pattern

    Prototype Pattern은 시간을 많이 소비하거나 또는 복잡한 클래스의 인스턴스를 생성할 때 사용된다. new Object()해서 많은 인스턴스를 생성하는 것 보다는 인스턴스 원본의 사본을 만들어 적당하게 ..

 

Limitation of Object Oriented


OO (Object Oriented) 얘기를 좀 해볼까 합니다. 질문의 시작은 디자인 패턴은 과연 현실에서 쓸모가 있는가? 라는 데서 출발합니다. OO 와 디자인 패턴은 따로 생각할 수가 없습니다. 디자인 패턴 자체가 OO 라는 사고의 틀 안에서 생겨났기 때문입니다. Design Patterns 의 Preface 에 다음과 같은 문장이 있네요.
It's a book of design patterns that describes simple and elegant solutions to specific problems in object-oriented software design.
OO 의 관점에 풀어낸 다양한 문제 패턴에 대한 솔루션이라는 이야기지요. 결국 디자인 패턴의 실제 효용성에 대한 질문은 OO 에 대한 것으로 옮겨갑니다.
OO 는 정말 쓸만 한 것인가?
아쉽게도 썩 그렇지는 못합니다. 코드의 재사용성 측면에서라면 더욱 그렇지 못합니다. 객체간의 상관관계는 프로젝트가 클 수록 exponentially 하게 복잡해집니다. 그런 관계 속에서 객체의 수정은 파급효과가 무시무시합니다. 디자인패턴이 나온 이유도 이것입니다. 요구사항 변화에 따른 기존 코드의 수정에서 조금이라도 더 자유로워지자 라는 거지요.

그럼에도 불구하고 OO 와 디자인 패턴을 공부해야하는 이유는 뭘까요? 전 이게 패러다임의 문제라고 봅니다. 토마스쿤은 과학혁명의 구조에서 과학의 발전단계를 다음과 같이 얘기했습니다.[fn]발전단계와 관련된 짧은 신문기사를 보실려면 다음 링크를 참고하세요. http://www.hani.co.kr/kisa/section-paperspcl/book/2005/07/000000000200507281939251.html [/fn]

정상과학 --> 위기 --> 혁명 --> 새로운 정상과학
저는 OO 가 점차 위기의 단계에 접어들고 있다고 봅니다.

애초 OO 는 프로그래밍을 하는 관점을 바꿔놓은 혁명이었습니다. 기계의 관점에서가 아니라 사람의 관점에서 프로그래밍이 가능하도록 해줬지요. 그러나 최초의 OOP 언어인 Smalltalk 이 나온지도 거의 30 년이 다되가다 보니, 이제 OO 의 패러다임은 점차 그 한계가 드러나기 시작하는 것 같습니다.

과학에서 위기의 고조뒤에 새로운 이론들이 나타났듯이, OO 라는 패러다임을 대체할 새로운 무언가가 나타날 거라 믿어 의심치 않습니다. 그것은 기존과는 전혀 다른 CPU 에 의해 나타날 수도 있고, 혁명적인 컴파일러에 의해 나타날 수도 있고, 또는 누군가가 만든 새로운 Programming Language 에 의한 것일 수도 있습니다. 심지어 기술분야에서가 아니라 Development management 에서 나타난 새로운 개발방법론에 의한 것일 수도 있지요.

OO 와 디자인 패턴을 공부해야 하는 가에 대한 제 대답을 요약하자면 이렇습니다.
  • 과학이 그러했듯, 기술에도 패러다임은 있다.
  • OO 라는 패러다임은 서서히 위기 단계로 넘어가고 있다.
  • 위기 고조에 의한 혁명이 일어나기 전까지 우리는 기존의 패러다임 내에서 활동한다.
  • 디자인 패턴은 OO 라는 패러다임내에서 효율적으로 보이는 솔루션들의 집합일 뿐이다.[fn]디자인 패턴 그 자체로 완벽한 솔루션은 아닙니다. 다음의 링크에서 관련 얘기를 들을 수 있습니다. http://crowmaniac.net/crowmania/?p=317 [/fn] 그것이 만병통치약은 아니다.
  • 다만, 디자인 패턴과 같은 것들에 대해 알고 겪고 난 뒤라야 비로소 기술적혁명을 맞이할 준비가 될 수 있을 것이다.

크리에이티브 커먼즈 라이센스
Creative Commons License
2007/04/29 22:11 2007/04/29 22:11
top

미투데이로 한마디트위터로 한마디페이스북에 한마디
Dev : 2007/04/29 22:11 Trackback. : Comment.

TRACKBACK ADDRESS :: http://www.gsong.pe.kr/tc/trackback/108

 

Abstract Factory Pattern


Factory Method 에서 한 가지 문제가 있었습니다. Concrete Creator 의 종류가 많아지면 애플리케이션의 코드 수정이 불가피하다는 것이었습니다. 디자인 패턴을 사용하는 목적에는 요구사항의 수정등이 있을 경우 change 가 파급되는 영역을 최소화하는 것도 있습니다.

자 그럼 결론 부터 보지요. 아래 class diagram 을 봐주세요.

Abstract Factory Pattern class diagram

Abstract Factory Pattern class diagram from Wikipedia (GPL licensed)

네 이게 Abstract Factory 입니다. Concrete Factory 들에 대해 부모 클래스를 하나 뒀지요. 부모 클래스는 Concrete Factory 들이 가지고 있어야할 인터페이스를 정의합니다.

Factory Method 패턴과 차이점이 뭘까요? Factory Method  에서 마지막에 나온 Application code 를 보면
[CODE]
// Application code
FruitStore  store =   GuyFruitStore();  
Fruit  f  = store.TakeMoneyAndGive( MySelf );
[/CODE]
라고 되어있었죠. 만약 과일상점이 GuyFruitStore() 가 아니라 새로 생긴 아줌만 과일 상점이 생기게 되어 월화수 에는 총각 가게를 가고 목금토일은 아줌마 가게를 간다고 합시다. 그럼 코드가 이렇게 되겠지요.
[CODE]
// Application code  
FruitStore  store;
if ( today == Mon || today == Tue || today == Wed)
    store =  new GuyFruitStore();
else
    store = new AzummaFruitStore();
Fruit  f  = store.TakeMoneyAndGive( MySelf );
[/CODE]
라고 수정이 됩니다. 게다가 새로운 가게가 생길 때마다 위의 if-else 문 의 종류가 코드 곳곳에서 점점 많아지게 되지요. 저런 분기문을 없애기 위해서 Abstract Factory 라는 상위 클래스를 정의하게 됩니다.

또, 총각 과일 가게에서 파는 사과와 아줌마 가게에서 파는 사과를 공통으로 다루기 위해서 각 Product 별로 상위 클래스를 역시 정의하게 되지요. 그림에서 AbstractProductA 와 AbstractProductB 가 그것입니다. 그럼 클라이언트 코드에서는 어떤 클래스들만 다루게 될까요?
[CODE]
AbstractFruitStore store = GetTodaysStore();
Fruite f = store.TakeMoneyAndGive( MySelf );
[/CODE]
네 이렇게 하면 GetTodaysStore() 라는 함수 안에서 어떤 과게에 가라고 지시가 나오는 대로 수행을 하면 되네요. 더 많은 과일가게 들이 생겨도 클라이언트 코드는 변경되지 않습니다. 다만 그때 그때 GetTodaysStore() 함수만 수정을 해주면 되지요.

Abstract Factory 의 장점은 총각 가게와 아줌마 가게가 쉽게 바뀐 것 처럼 Factory 간의 교체가 쉽다는 점입니다. 한편 단점이라고 한다면, 바로 AbstractProduct 클래스입니다. Factory 에서 생성해내는 종류별로 존재해야 되기 때문에 번거롭다는 단점이 있지요.

크리에이티브 커먼즈 라이센스
Creative Commons License
2007/04/21 18:38 2007/04/21 18:38
top

미투데이로 한마디트위터로 한마디페이스북에 한마디

TRACKBACK ADDRESS :: http://www.gsong.pe.kr/tc/trackback/104

 

Factory Method


디자인 패턴에서 맨 처음 마주치게 되는 패턴중 하나인 팩토리 메소드 패턴입니다. 객체로 만들 클래스를 애플리케이션에서 명시하지 않아도 되는 장점이 있습니다. 아래 그림을 한번 보죠.
Factory method Pattern

Factory method Pattern from Wikipedia

Creator 는 팩토리 역할을 할 ConcreteCreator 의 공통 인터페이스를 정의해 놓은 부모 클래스 입니다. ConcreteCreator는 이것을 상속 받아 factoryMethod() 메소드를 구현하게 됩니다.

간단한  예를 한번 들어볼까요? 아래 pseudo code 를 한번 보지요.
[CODE]
abstract class FruitStore
{
    abstract Fruit   TakeMoneyAndGive(Guest g);
}
[/CODE]
과일가게에 대한 Interface 를 정의해 봤습니다. 가게 주인이 하는 일은 돈을 받고 물건을 건네주는 TakeMoneyAndGive() 라는 메소드가 있네요. 자 이제 과일가게의 ConcreteCreator 에 해당하는 'GuyFruitStore' 을 한번 만들어보지요.

[CODE]
class GuyFruitStore inherits FruitStore
{
    Fruit  TakeMoneyAndGive(Guest g)
    {
        if  g is a girl
            return new GoodFruit
        else
            return new BadFruit
    }
}

class GoodFruit inherits Fruit
{
}

class BadFruit inherits Fruit
{
}
[/CODE]
이 노총각 가게 주인은 동네 아가씨들에게는 달고 맛있는 과일을 주고 그 외의 사람들에게는 싱싱하지 않은 과일을 주네요. 이제 Factory Method 가 어떤 장점을 가지는 지 알아봅시다. '노총각 가게에서 과일을 산다'  라는 문장을 코드로 바꿔보면
[CODE]
// Application code
FruitStore  store =  new GuyFruitStore();
Fruit  f  = store.TakeMoneyAndGive( MySelf );
[/CODE]
과일을 사러 간 본인이 Girl 이라면 f 변수는 GoodFruit 가 될 것이고, 할아버지라면 BadFruit 의 instance 가 저장될 것입니다. 만약 추후에 노총각의 마음씨가 고와져서 귀여운 꼬마의 경우에도 좋은 과일을 주기로 마음 먹었다고 해도, '노총각 가게에서 과일을 산다' 라는 코드는 수정할 필요가 없게 되지요. 수정은 오로지 ConcreteCreator 인 GuyFruitStore 클래스 내에서만 이뤄집니다.
[CODE]
class GuyFruitStore inherits FruitStore
{
    Fruit  TakeMoneyAndGive(Guest g)
    {
        if  g is a girl  or cute kid
            return new GoodFruit
        else
            return new BadFruit
    }
}
[/CODE]

이 처럼 Factory Method pattern 은 객체의 생성을 위임할 수 있어 요구사항 수정에 따른 애플리케이션의 변경을 줄일 수 있다는 장점이 있습니다. 하지만, 과일가게 종류가 늘어날 경우에는 문제가 되지요. 노처녀과일가게, 아줌마과일가게 등이 우후죽순으로 생길 경우는 애플리케이션코드의 수정이 불가피합니다. 이 문제를 해결하기 위해 Factory Method pattern 을 조금 더 추상화한 Abstract factory 라는 패턴이 나오게 됩니다.
크리에이티브 커먼즈 라이센스
Creative Commons License
2007/04/16 22:35 2007/04/16 22:35
top

미투데이로 한마디트위터로 한마디페이스북에 한마디

TRACKBACK ADDRESS :: http://www.gsong.pe.kr/tc/trackback/105

  1. Tracked from Study&Joy 2010/05/21 20:49 DELETE

    Subject: [디자인 패턴] Factory Method

    <출처 : http://www.gsong.pe.kr/tc/105> 디자인 패턴에서 맨 처음 마주치게 되는 패턴중 하나인 팩토리 메소드 패턴입니다. 객체로 만들 클래스를 애플리케이션에서 명시하지 않아도 되는 장점이 있습니다. 아래 그림을 한번 보죠. Factory method Pattern from Wikipedia Creator 는 팩토리 역할을 할 ConcreteCreator 의 공통 인터페이스를 정의해 놓은 부모 클래스 입니다. ConcreteC..