Iterator Pattern( 이터레이터 패턴 ) 디자인패턴

[ Iterator Pattern( 이터레이터 패턴 ) 정의 ]
컬렉션 구현 방법을 노출시키지 않으면서도 그 집합체 안에 들어있는 모든 항목에 접근 할 수 있게 해주는 방법을 제공해 준다.

[ 클래스 다이어그램 ]

[ 시나리오 ]
팬케이크하우스와 마을식당이 합병하기로 했다. 이제 웨이트리스는 방문한 고객한테 모든 메뉴를 출력해서 보여줘야 한다. 그런데 팬케이크하우스 메뉴는 std::vector에 저정했고, 마을식당은 메뉴는 배열에 저장해서 사용하고 있다. 어떻게 하면 기존의 코드는 변경하지 않고, 서로 다른 저장구조를 가지고 있는 두 메뉴를 출력하여 고객한테 보여줄 수 있을까?

[ 디자인원칙 ]
클래스를 바꾸는 이유는 한 가지 뿐이어야 한다.
만약 클래스 내부에서 반복자용 메소드 관련기능을 전부 구현하면 어떻게 될까? 만약 클래스의 역할이 2가지 이상이 되면, 그 2가지 이유로 인해서 클래스가 변경될 수 있다. 컬렉션이 어떤 이유로 변경되거나 반복자 관련 기능이 변경되었을 때도 클래스를 수정해야 한다. 이처럼 클래스가 2가지 역할을 하게되면 응집도(Cohesion)이 떨어지게 된다. 여기서 응집도(Cohesion)란 한 클래스 또는 모듈이 특정 목적 또는 역할을 얼마나 일관되게 지원하는지를 나타내는 척도이다. 어떤 모듈 또는 클래스의 응집도가 높다는 것은 일련의 서로 연관된 기능이 묶여 있다는 것을 응집도가 낮다는 것은 서로 상관없는 기능들이 묶여있다는 것을 뜻한다.

[ 요약 ]
Iterator 클래스는 PancakeHouseMenu와 DinerMenu 클래스에 있는 원소집합을 랩퍼(Rapper) 작업하게 된다. 이렇게 만들어진 PancakeHouseMenuIterator, DinerMenuIterator 클래스는 Waitres에 의해서 출력된다. Waitres 클래스는 동일한 인터페이스로 서로다른 두 클래스의 원소집합에 일일이 접근할 수 있게 된다.

[ 구현코드 ]
#include "stdafx.h"
#include <iostream>
#include <string>
#include <vector>

using namespace std;

class MenuItem;

class Iterator
{
public:
    virtual bool    HasNext( void ) = 0;
    virtual void*   Next( void ) = 0;
};

class PancakeHouseMenuIterator : public Iterator
{
public:
    PancakeHouseMenuIterator( std::vector<MenuItem*>& refVecMenuItems )
    {
        m_iPosition = 0;
        m_vec_MenuItems = refVecMenuItems;
    }

public:
    virtual bool    HasNext( void )
    {
        if( m_iPosition < m_vec_MenuItems.size() )
            return true;

        return false;
    }

    virtual void*   Next( void )
    {
        MenuItem* pMenuItem = m_vec_MenuItems.at( m_iPosition );
        ++m_iPosition;

        return pMenuItem;
    }

private:
    std::vector<MenuItem*>  m_vec_MenuItems;
    int                     m_iPosition;
};

class DinerMenuIterator : public Iterator
{
public:
    DinerMenuIterator( MenuItem* pMenuItem[] )
    {
        m_iPosition = 0;
        for( int i=0; i<MAX_ITEM_MENU; i++ )
        {
            m_pMenuItem[i] = pMenuItem[i];
        }
    }

public:
    virtual bool    HasNext( void )
    {
        if( m_iPosition < MAX_ITEM_MENU )
            return true;

        return false;
    }

    virtual void*   Next( void )
    {
        MenuItem* pMenuItem = m_pMenuItem[ m_iPosition ];
        ++m_iPosition;

        return pMenuItem;
    }

private:
    enum { MAX_ITEM_MENU = 3 };
    MenuItem*   m_pMenuItem[ MAX_ITEM_MENU ];
    int         m_iPosition;
};

class MenuItem
{
public:
    MenuItem( string szName, string szDescription, double dPrice )
    {
        m_szName = szName;
        m_szDescription = szDescription;
        m_dPrice = dPrice;
    }

public:
    string  GetName( void ) { return m_szName; }
    string  GetDescription( void ) { return m_szDescription; }
    double  GetPrice( void ) { return m_dPrice; }

private:
    string  m_szName;
    string  m_szDescription;
    double  m_dPrice;
};

class PancakeHouseMenu
{
public:
    PancakeHouseMenu( void )
    {
        AddItem( "팬케이크 세트", "스크램블드 에그와 토스트가 곁들여진 패케이크", 2.59 );
        AddItem( "레귤러 팬케이크 세트", "달걀 후라이와 소시지가 곁들여진 팬케이크", 2.19 );
        AddItem( "블루베리 팬케이크", "신선한 블루베리와 블루베리 시럽으로 만든 팬케이크", 3.49 );
    }

public:
    void    AddItem( string szName, string szDescription, double dPrice )
    {
        MenuItem* pMenuItem = new MenuItem( szName, szDescription, dPrice );
        m_vec_MenuItems.push_back( pMenuItem );
    }

    Iterator*   CreateIterator( void ) { return new PancakeHouseMenuIterator( m_vec_MenuItems ); }

private:
    typedef std::vector<MenuItem*>  STL_VEC_MENUITEM;
    STL_VEC_MENUITEM                m_vec_MenuItems;
};

class DinerMenu
{
public:
    DinerMenu( void )
    {
        m_iNumberOfItem = 0;
        AddItem( "오늘의 스프", "감자를 곁들인 오늘의 스프", 3.29 );
        AddItem( "핫도그", "갖은 양념, 양파가 곁들여진 핫도그 ", 3.05 );
        AddItem( "햄버거", "베이컨, 상추, 토마토를 얹은 햄버거", 3.99 );
    }

public:
    void    AddItem( string szName, string szDescription, double dPrice )
    {
        if( m_iNumberOfItem > MAX_ITEM_MENU - 1 )
        {
            cout<<"더 이상 추가 할 수 없습니다."<<endl;
            return;
        }

        m_pMenuItem[ m_iNumberOfItem ] = new MenuItem( szName, szDescription, dPrice );
        ++m_iNumberOfItem;
    }

    Iterator*   CreateIterator( void ) { return new DinerMenuIterator( m_pMenuItem ); }

private:
    enum { MAX_ITEM_MENU = 3 };
    MenuItem*   m_pMenuItem[ MAX_ITEM_MENU ];
    int m_iNumberOfItem;
};

class Waitress
{
public:
    Waitress( PancakeHouseMenu* pPancakeHouseMenu, DinerMenu* pDinerMenu )
    {
        m_pPancakeHouseMenu = pPancakeHouseMenu;
        m_pDinerMenu = pDinerMenu;
    }

public:
    void    PrintMenu( void )
    {
        Iterator* pPancakeHouseMenuIterator = m_pPancakeHouseMenu->CreateIterator();
        Iterator* ppDinerMenuIterator = m_pDinerMenu->CreateIterator();

        cout<<"[아침메뉴]"<<endl;
        Print( pPancakeHouseMenuIterator );
        cout<<endl;

        cout<<"[점심메뉴]"<<endl;
        Print( ppDinerMenuIterator );
        cout<<endl;
    }

private:
    void    Print( Iterator* pIterator )
    {
        while( pIterator->HasNext() )
        {
            MenuItem* pMenuItem = (MenuItem*)pIterator->Next();
            cout<<pMenuItem->GetName()<<" $"<<pMenuItem->GetPrice()<<" - "<<pMenuItem->GetDescription()<<endl;
        }
    }

private:
    PancakeHouseMenu*   m_pPancakeHouseMenu;
    DinerMenu*          m_pDinerMenu;
};

int _tmain( int argc, _TCHAR* argv[] )
{
    PancakeHouseMenu* pPancakeHouseMenu = new PancakeHouseMenu;
    DinerMenu* pDinerMenu = new DinerMenu;

    Waitress* pWaitress = new Waitress( pPancakeHouseMenu, pDinerMenu );
    pWaitress->PrintMenu();
}

[ 결과화면 ]

트랙백

이 글과 관련된 글 쓰기 (트랙백 보내기)
TrackbackURL : http://liepooh.egloos.com/tb/1101291 [도움말]

덧글

댓글 입력 영역


통계 위젯 (화이트)

143
364
10347