'정보은닉'에 해당되는 글 1건

  1. 2009.04.09 정보은닉을 위한 라이브러리 헤더
dev.log2009. 4. 9. 12:59
라이브러리, 특히 C++ 라이브러리 배포 시에 인터페이스'만' 배포하는 것은 상당히 귀찮은 일이다. 나같은 경우는 클래스에 nested type을 무척 많이 쓰고, 일부는 구현에만 사용하는 타입이기 때문에 nested type이 외부로 나가면 내부 구현을 까발리는 것과 다를 바가 없다. 물론 private 권한으로 숨겨놓을 수도 있지만, 뻔히 헤더 소스에 보이는 걸 숨겨놨다고 하기엔 옹색한 것도 사실이다. 그래서 만들고 있는 파일 스트림 라이브러리는 다음과 같이 하기로 했다.

배포용 헤더. 바이너리 라이브러리만 배포할 때는 요놈만 쓴다.
class stream
{
public:
    static stream* create( const string& strm_path );
    static stream* create();
    virtual ~stream();

    virtual int open( const string& strm_path ) =0;
    virtual int close() = 0;
    virtual int read( void* buffer, size_t size ) = 0;
    virtual int write( void* buffer, size_t size ) = 0;
    virtual size_t tell() = 0;
    virtual int seek( size_t offset ) = 0;

protected:
    stream() {}
    stream( const stream& ) {}
};
기본 생성자와 기본복사생성자를 protected로 막아놓고 Create로 쓰는 이유는, 어디까지나 인터페이스이기 때문. 구현 레벨에서 다형성 타입으로 생성하도록 한다.


구현용 헤더
class stream_impl
{
    virtual int open( const string& strm_path ) =0;
    virtual int close() = 0;
    virtual int read( void* buffer, size_t size ) = 0;
    virtual int write( void* buffer, size_t size ) = 0;
    virtual size_t tell() = 0;
    virtual int seek( size_t offset ) = 0;
};

class stream_file : public stream_impl
{
public:
    //...
protected:
    fstream _file;
};

class archive;
class archive_item;
class stream_pack : public stream_impl
{
public:
    //...
protected:
    archive* _pack;
    archive_item* _item;
};

class stream_class : public stream
{
public:
    stream_class();
    stream_class( stream_impl* impl );
    //...
protected:
    stream_impl* _impl;
};

class archive
{
    int open_pack( const string& path );
    int close_pack();

    archive_item* open( const string& path );
    int close( archive_item* item );
    int read( .. );
    int write( .. );
    int seek( .. );
    int tell( .. );
};
stream_class와 stream_impl을 분리한 이유는, 동일한 stream 객체를 서로 다른 종류의 스트림으로 다시 열 수 있도록 하기 위해서.

구현용 소스는 대략 다음과 같은 모양새가 된다.
stream* stream::create( const string& strm_path )
{
    stream_class* strm = new stream_class;
    strm->open( strm_path );
    return strm;
}

int stream::open( const string& strm_path )
{
    if ( _impl )
    {
        delete _impl;
        _impl = 0;
    }

    if ( search_package( strm_path ) )
        _impl = new stream_pack;
    else if ( search_file( strm_path ) )
        _impl = new stream_file;
    else
        return 0;

    return _impl->open( strm_path );
}

약간 귀찮긴 하지만 '싫어하는 사람'에게 라이브러리를 배포해야 할 때에는 유용할지도.. 생각해보면 위와 같은 모양새를 가리키는 디자인 패턴의 이름도 있을 거 같긴 한데, 찾아보기 귀찮다. ( ..)

Posted by uhm