본문 바로가기

프로그램언어/GoF

Build 패턴

 
사용자 삽입 이미지
< 그림1. Build 패턴 >


일반 함수 형태의 접근, 즉 구조적 개념(structured Paradigm)에 기반한 접근이 가지는가장 큰 문제 중의 하나는 바로 요구사항의 추가, 변경 시 영향을 받는 범위가 불분명하고, 모듈의 재사용 시에도 재사용할 모듈의 경계가 불확실하다는 것이다.

이런 문제를 해결하기 위해 등장한 것이 바로 객체지향 개념(Object oriented paradigm) 에 기반한 접근 방식이다. 이 방식은 명확히 구분되는 클래스를 기준으로 소프트웨어의 변경이나 재사용이 가능하게 만들어주는데, 이를 메뉴얼 번역을 위한 소프트웨어에 적용한다.

패턴 정리 :


UML 설명 :

전 세계에 수출되는 전자 제품을 만드는 회사에서 메뉴얼을 만들어 배포하는 소프트웨어를 개발하기로 결정한다. 이때 개발해야 할 소프트웨어는 한국어, 영어, 일본어, 프랑스어로 된 메뉴얼을 만들며, 문장에는 평서문, 의문문, 명령문으로만 구성되어 있으며, 자동 번역 소프트웨어는 메뉼얼을 문장 단위로 번역하면 된다.

소스설명 :

// main.cpp

#include <iostream>
#include <string>
#include <fstream>
#include "Sentence.h"
//using namespace std;


int main() {

 Director dir;
 EnglishPublic eng;

 dir.DoTranslate("e_book.txt", eng);

 Manual out = eng.GetResult();
 
 std::cout << out.GetContents() << std::endl;

 return 0;
}

//sentence.h
/*
* Sentence.h
*
* Creator : 에블릿
* Creation date : 2008. 07. 28
*
* Copyright (c) 2008 에블릿
* All rights reserved.
*
* This software is the confidential and proprietary information of
* 에블릿. ("Confidential Information").  You shall not disclose such
* Confidential Information and shall use it only in accordance with
* the terms of the license agreement you entered into with 에블릿.
*/

#include <iostream>
#include <fstream>


/*
 *  @br Language set incording information.
 * @auth 에블릿.
 * @date 2008.7.28
 */
const int UNDEF_SENTENCE=0;
const int NORMAL_SENTENCE=1;
const int INTERROGATIVE_SENTENCE=2;
const int IMPERATIVE_SENTENCE=3;
const int TO_ENGLISH=1;
const int TO_CHINA=2;
const int TO_KOREA=3;


/**
 *  문장에 대한 정보을 갱신해주는 클래스.
 *
 * @author   에블릿
 * @date     2008.07.28
 * @todo   
  *
 * <p>
 */
class Sentence {
public:
 Sentence();
 ~Sentence();

public:
 int GetType();
 std::string GetString();

 void SetSentenceData(std::string s) {
      //SetSenteceType(s);
      data_a = s;
    }
private:
 std::string data_a;
 int type;
};


/**
 *  메뉴얼에 대한 클래스.
 *
 * @author   에블릿
 * @date     2008.07.28
 * @todo   
  *
 * <p>
 */
class Manual {
public:
 Manual();
 ~Manual();
  public :
 std::string GetContents();
 void AddContents(std::string s);

  private :
   std::string contents_;
};


/**
 *  Build 패턴에서 추상 클래스 변환.
 *
 * @author   에블릿
 * @date     2008.07.28
 * @todo   
  *
 * <p>
 */
class Translator {
public:
 Translator();
 virtual ~Translator();

 Manual GetResult();
 virtual void TransNormalSentence(std::string s) =0;
 virtual void TransInterrogativeSentance(std::string s) = 0;
 virtual void TransImperativeSentence(std::string s ) = 0;

protected:
 Manual _result;

};

class EnglishPublic : public Translator {
public:
 EnglishPublic();
 virtual ~EnglishPublic();

 void TransNormalSentence(std::string s );
 void TransInterrogativeSentance( std::string s );
 void TransImperativeSentence( std::string s );
};
/**
 *  상위에 있는 클래스.
 *
 * @author   에블릿
 * @date     2008.07.28
 * @todo   
  *
 * <p>
 */
class Director {
public:
 Director();
 virtual ~Director();

 void DoTranslate( char* pInFile, Translator& t);

protected:
 Sentence GetSentance(std::ifstream& ifs);
};

// sentence.cpp
#include "Sentence.h"
#include <fstream>
/*
문장의 초기화 */
Sentence::Sentence() {
 data_a = "";
 type = NORMAL_SENTENCE;
}

/*
문장의 파괴자 */
Sentence::~Sentence() {
}

/*
Type 정보를 가져올 때 */
int Sentence::GetType() {
 return type;
}

/*
data_a  정보를 가져올 때 */
std::string Sentence::GetString() {
 return data_a;
}

/*
메뉴얼 생성자 및 초기화 */
Manual::Manual() {
 contents_ = "";
}

/*
메뉴얼 파괴자 */
Manual::~Manual() {
}

/*
메뉴얼 문장을 가져올 때 */
std::string Manual::GetContents() {
 return contents_;
}

/*
메뉴얼 문장에 대해 붙여 쓰기 할때 사용 */
void Manual::AddContents(std::string s) {
 contents_ += s;
}

/*
추상 클래스 정의에 대한 생산자 */
Translator::Translator() {
}

/*
추상 클래스 정의에 대한 파괴자 */
Translator::~Translator() {
}

/*
추상 클래스에 대해 결과 값을 받고자 할 때 */
Manual Translator::GetResult() {
 return _result;
}

/*
최상의 클래스의 생산자 */
Director::Director() {
}

/*
최상의 클래스의 파괴자 */
Director::~Director() {
}

/*
파일에서 읽어 드린 파일 정보를 분류하여 정보를 담을 때 */
void Director::DoTranslate(char *pInFile, Translator &t) {

 std::ifstream ifs(pInFile);
 if ( !ifs ) {
  std::cout << "Can't File open Failed!!" << std::endl;
  return;
 }

 Sentence next;
 while ( !(next=GetSentance(ifs)).GetString().empty() ) {
  switch( next.GetType() ) {
   case NORMAL_SENTENCE:
    t.TransNormalSentence( next.GetString() );
    break;
   default:
    break;
  }
 }
}

/*
최상의 클래스의 의 문장을 가져올 때 사용한다. */
Sentence Director::GetSentance(std::ifstream &ifs) {

 int c;
 std::string s;
 Sentence sentance;
 while(  ( c=ifs.get() ) != EOF ) {
  s += c;
  if ( c == '?' || c == '.' )
   break;
 }
 sentance.SetSentenceData( s );

 return sentance;
}


/*
EnglishPublic 영문판으로 출판에 대한 클래스의 초기화 및 생성자 */
EnglishPublic::EnglishPublic() {
}

/*
EnglishPublic 영문판으로 출판에 대한 클래스의 파괴자 */
EnglishPublic::~EnglishPublic() {
}

/*
EnglishPublic 영문판 번역 출판을 위한 함수 */
void EnglishPublic::TransNormalSentence( std::string s ) {
 std::string output;

 // 영문번역을 통한 함수.
 output = s;

 _result.AddContents( output );
}

void EnglishPublic::TransInterrogativeSentance( std::string s ) {
 std::string output;
 output = s;
 _result.AddContents( output );
}

void EnglishPublic::TransImperativeSentence( std::string s ) {
 std::string output;
 output = s ;
 _result.AddContents( output );
}




패턴 응용 :
Build 패턴을 다음과 응용하고자 한다. 게임에서의 승직을 예로 다음과 같이 만들고자 한다.

설명 :
게임의 캐릭터는 일정한 렙에 도달하게 되면 승직을 하게 된다. 그 일정한 레벨업에 따라서 1차 승직, 2차 승직, 3차 승직, 4차 승직, ... 승직을 하게 되므로 적절한 패턴을 이용하여 간단한 샘플을 만든다.
 
사용자 삽입 이미지
< 그림2. Build 패턴의 응용 >


소스 및 설명 :

Note:
Builder 패턴은 다음과 같은 경우에 유용하다.
 - 복잡한 객체를 생성하는데 있어 그 객체를 구성하는 부분 부분들을 생성한 후 그것을 조합해도 객체의 생성이 가능할 때, 즉 객체의 부분 부분을 생성하는 것과 그들을 조합해서 전체 객체를 생성하는 것이 서로 독립적으로 이루어질 수 있는 경우.
- 서로 다른 표현 방식을 가지는 객체를 동일한 방식으로 생성하고 싶을 때, 즉 번역 소프트웨어에서 본 것처럼 번역 대상 언어가 달라도 객체들을 동일한 방식으로 생성하고 싶을 경우.

끝으로, 이상의 Builder 패턴에 대하 장단점을 정리하면 다음과 같다.
- Builder 클래스는 슈퍼 클래스에게 객체를 생성 할 수 있는 인터페이스를 제공한다. 대신 생성되는 객체의 내부 구조나 표현 방식, 조합 방법등은 Builder 클래스 내부로 숨긴다. 이런 형기 때문에 빌더패턴에서는 생성되는 객체의 내부 구조를 변경시킬 필요가 있는 경우 기존 소스코드에는 영향을 주지 않고, 새로운 하위 클래스를 추가 정의하면 원하는 작업을 수행을 할 수 있는 장점을 가지고 있다.
- 빌드패턴은 객체를 생성하는 부분과 그것을 실제 표현하고 구성하는 부분을 분리시켜줌으로써 서로 간의 독립성을 기할 수 있게 해준다.
- 빌드패턴의 경우에는 객체를 한꺼번에 생성하는 것이 아니라, 부분 부분을 생성한 후 최종 결과를 얻어가는 방식이므로, 객체 생성 과정을 좀더 세밀히 제어할 수 있다. 예를 들어 앞에서도 언급하였듯이 번역 소프트웨어 개발의 경우 아시아 지역에만 필요한 메뉴얼 내용은 영어나 프랑스어로 번역 시 에는 포함시키지 않는 것등이 가능하다.
- 빌드 패턴은 새로운 종류의 객체 생성을 추가하기는 쉬우나, 객체를 구성하는 각 부분들을 새롭게 추가하기는 어렵다. 예를 들어 번역 소프트웨어 개발에서 평서문, 의문문, 명령문,만 번역하는 것을 감탄문까지 번역 할 수 있게 추가한다는 것이 가능하다.


출처 : GoF 디자인 패턴 이렇게 활용하기 책을 보고 UML로 그려 넣은것이다.

설명이 틀리거나 수정사항에 대해서는 댓글로 지적해 주시면 감사하겠습니다.

'프로그램언어 > GoF' 카테고리의 다른 글

Factory Method 패턴 < 작성중 >  (0) 2008.08.06
GoF 디자인 패턴 이렇게 활용하기  (0) 2008.07.25