아래의 글은 언제나 휴일 출판사의 장문석님의 "Escort GoF의 디자인 패턴" 도서의 내용을 정리한 것 입니다. (http://cafe.daum.net/ehclub.net/S8l6/11)

 

빌더 패턴(Builder Pattern)

 

1.1 개요

 

프로그래밍을 하다 보면 개체의 형식 변환을 해야 하는 경우가 발생합니다. 경우에 따라서는 소유 개체에서 피 소유 개체를 목적에 따라 변환 알고리즘을 다르게 적용하여 형식 변환을 해야 되는 경우가 있습니다. 이러한 경우에 빌더 패턴을 사용하던 소유 개체에 대한 클래스와 알고리즘에 대한 클래스를 분리하여 정의할 수 있습니다. 'GoF의 디자인 패턴'에서는 소유 개체를 디렉터라 부르고 알고리즘이 구현된 개체를 빌더라 부르고 있습니다.

 

빌더 패턴에서 빌더는 추상 클래스로 표현하여 개체를 생성하는 메서드에 대한 약속을 합니다. 파생 클래스에서는 약속된 메서드를 목적에 맞는 알고리즘을 사용하여 구체적으로 구현을 해야합니다.

소유 개체를 구현함에 있어 피 소유 개체를 생성하는 알고리즘이 다양할 경우 빌더 패턴을 사용하면 효과적입니다. 차후에 다른 형태의 알고리즘을 적용하여 피 소유 개체를 생성해야 하는 경우에 빌더 클래스에서 파생된 구체화 된 클래스만 추가하면 될 것입니다.

 

1.2 시나리오

 

우리 가족은 매주 토요일에는 여행을 다녀옵니다. 여러분들도 아시다시피 우리 가족은 사진을 찍는 것을 좋아합니다. 이러한 생활을 하다보니 사랑하는 아들 혁재는 자연스럽게 카메라와 사진에 대해 많은 호기심이 생기나 봅니다.

 

"아빠, 어떻게 해서 카메라로 사진을 찍을 수 있는거야?"

 

아들에게 렌즈에 대한 얘기와, 초점 빛에 대한 얘기를 하다보니 지겨운지 자기 방으로 들어가더군요. 이제껏 대부분의 아들의 호기심을 해소시켰다고 자부했었는데 내가 좋아하는 카메라에 대해 설명을 못해 안타까웠어요. 밤 새 고민을 하다 근처에 있는 EHCamera 회사를 견학하면 좋겠다는 생각을 하게 되었고 견학에 필요한 것이 무엇인지 확인하고 견학 신청을 하였습니다.

 

몇 일 뒤에 우리 가족은 EHCamera 회사를 방문했어요. 회사 입구에서부터 안내원이 반갑게 맞아주었고 회사에 대한 간단한 소개와 생산 라인을 견학할 수 있었어요. 그리고, 엔지니어와의 만남의 시간이 주어졌는데 그가 건네준 명함에는 '이 매핑'이라는 이름이 적혀있더군요.

 

"매핑님, 카메라에 상이 맺혀 사진이 되는 과정이 어떻게 되나요?"

"음, 카메라로 사진을 찍을 수 있는 원리는 사람의 눈으로 사물들을 보는 원리와 흡사합니다. 우리가 사물을 볼 수 있는 것은 빛이 사물에 부딪혀 반사되는 것이 눈에 그려지는 것입니다. 쉽게 얘기하면 빛이 그림을 그리는 것이지요."

"아빠, 학교에서 배웠어. 빛은 여러 가지 색을 지니고 있다고 배웠어. 그리고, 실험 시간에 스펙트럼을 통해 빛의 반사에 대해 알게 되었어. 카메라도 사람의 눈과 마찬가지구나."

매핑씨는 혁재가 하는 말을 듣고 좀 더 자세한 얘기를 해주었어요. 조리개를 열어 빛을 얻어오는 것과 반사경을 통해 상이 맺히는 것에 대하여 알게 되었습니다. 그리고, 설정에 따라 얻어온 상을 이미지로 변환하는 원리도 얘기해 줬어요. 그리고, 이 부분은 소프트웨어적으로 처리를 한다고 하더군요.

 

"아빠, 그럼 아빠도 저런 거 만드는 거야?"

"음. 비슷해"

 

그리고, 집에 돌아와서 카메라 효과에 관련된 자료를 조사 했어요. 조명에 의해 적목 현상을 어떻게 개선을 시키는 지도 알게 되었고 피사체가 사람인 경우에는 윤곽선을 부드럽게 해 주는 원리에 대해서도 알게 되었죠. 그리고, 새로운 카메라에 기존의 효과 외에 다른 효과들을 손쉽게 하기 위한 방법을 알게 되었어요. 이는 각각의 효과마다 별도의 모듈을 사용하고 각각의 모듈은 동일한 인자를 입력 받아 그림을 만들어 내는 공통적인 인터페이스를 갖게 하면 됩니다.

 

1.3 디자인

 

이번에는 카메라와 사진, 사진 빌더로 빌더 패턴에 대한 예를 들려고 합니다. 카메라는 사진을 찍는 기능이 있고 효과를 줄 수 있게 하려고 합니다. 대신 효과를 주는 부분을 사진 빌더를 통해 수행하게 할 것입니다.

 

카메라는 디렉터 역할을 하며 Camera 이름의 클래스로 구현을 하겠습니다.

 

사진은 카메라를 통해 사용자가 얻고자 하는 개체로 Picture라고 정하겠습니다.

 

사진 빌더는 추상클래스로 PictureBuilder라 하겠습니다. 사진 빌더에서 파생된 클래스는 피사체의 날카로운 부분을 매끈하게 바꾸어 사진을 생성하는 SmoothBuilder가 있습니다. 그리고, 피사체의 적목 현상이 발생하는 것을 보정하여 사진을 생성하는 REPeventBuilder도 추가하겠습니다.

[그림 1] 빌더 패턴 설계 예

 

1. 4 구현

 

이제 빌더 패턴에 대한 예제 프로그램을 구현해 봅시다. 구현 순서는 사진, 사진 빌더, 카메라 순으로 하겠습니다.

 

2.4.1 사진

 

사진은 사진의 이름을 가지고 있는 클래스로 생성합니다.

namespace AboutBuilderPattern

{

class Pictrure

{

string Name;

public Pictrure(string _name)

{

this.Name = _name;

}

public override string ToString()

{

return this.Name;

}

}

}

 

2.4.2 사진 빌더

 

사진 빌더는 피사체를 입력 매개변수로 받아 특정 효과를 거쳐 사진을 생성하는 역할을 수행합니다. 추상 클래스 PictureBuilder에서는 이에 대한 약속을 해야합니다. 효과를 주어 사진을 생성하는 기능을 Effect라 하겠습니다. 그리고, 입력 인자로 전달 받을 피사체의 형식을 string 형식을 사용하겠습니다.

 

namespace AboutBuilderPattern

{

abstract class PictureBuilder

{

public abstract Pictrure Effect(string orgin);

}

}

 

PictureBuilder로 부터 파생된 클래스 SmoothBuilder에서는 기반 클래스에서 약속한 Effect 메서드를 구현해야 합니다. 그리고, Effect 메서드에서는 날카로운 부분을 찾는 과정과 매끈하게 보정하는 작업이 필요할 것 입니다. 이들 기능에 대한 이름은 FindSharp, SharpToSmooth라고 정하겠습니다.

 

namespace AboutBuilderPattern

{

class SmoothBuilder:PictureBuilder

{

const string SMOOTH = "Smooth";

const string SHARP = "Sharp";

 

int FindSharp(string origin)

{

return origin.IndexOf(SHARP);

}

Pictrure SharpToSmooth(string origin, int index)

{

int flen = SHARP.Length;

string front = origin.Substring(0, index);

string last = origin.Substring(index + flen, origin.Length - index - flen);

return new Pictrure(front + SMOOTH + last);

}

 

public override Pictrure Effect(string orgin)

{

int index = FindSharp(orgin);

return SharpToSmooth(orgin,index);

}

}

}

 

PictureBuilder에서 파생된 또 다른 클래스 REPreventBuilder에서도 Effect 메서드를 구현해야 할 것입니다. 그리고, Effect 메서드에서는 적목 현상이 있는 눈의 위치를 보정하는 작업이 필요합니다. 이들 기능에 대한 이름은 FindRedEye, RedEyeToNormalEye라고 정하겠습니다.

 

namespace AboutBuilderPattern

{

class REPreventBuilder:PictureBuilder

{

const string RED_EYE = "RedEye";

const string NORMAL_EYE = "NormalEye";

 

public override Pictrure Effect(string orgin)

{

int index = FindRedEye(orgin);

return RedEyeToNormalEye(orgin, index);

}

 

int FindRedEye(string orgin)

{

return orgin.IndexOf(RED_EYE);

}

Pictrure RedEyeToNormalEye(string origin, int index)

{

int flen = RED_EYE.Length;

string front = origin.Substring(0, index);

string last = origin.Substring(index + flen, origin.Length - index - flen);

return new Pictrure(front + NORMAL_EYE + last);

}

}

}

 

2.4.3 카메라

 

카메라에는 셔터를 누르면 피사체를 사진으로 만들어주는 기능이 있어야 할 것입니다. 이러한 기능을 Effect Normal이라고 하겠습니다. 그리고, 찍은 사진을 필요할 때 얻어오는 기능도 있으면 사용자는 편리할 것입니다. 이러한 기능은 GetPicture 라고 합시다. 이 외에도 카메라에 줄 수 있는 효과들에 대한 열거형을 정의하여 사용을 한다면 좀 더 효과적으로 사용 할 수 있을 것입니다.

 

namespace AboutBuilderPattern

{

enum EffectType

{

EF_PREVENTRE=0,

EF_SMOOTH=1,

EF_NORMAL = 2,

EF_SMOOTH_PRERE=3

}

 

 

class Camera

{

PictureBuilder []pbs;

Pictrure picture;

 

public Camera()

{

pbs = new PictureBuilder[2];

pbs[0] = new REPreventBuilder();

pbs[1] = new SmoothBuilder();

picture = new Pictrure(string.Empty);

}

 

public Pictrure PressAShutter(string origin, EffectType effect)

{

switch (effect)

{

case EffectType.EF_NORMAL: EffectNormal(origin); break;

case EffectType.EF_PREVENTRE: PreventRedEye(origin); break;

case EffectType.EF_SMOOTH: Smoothing(origin); break;

case EffectType.EF_SMOOTH_PRERE: SmoothPrere(origin); break;

}

return GetPicture();

}

 

public Pictrure GetPicture()

{

return picture;

}

 

private void SmoothPrere(string origin)

{

Smoothing(origin);

PreventRedEye(origin);

}

 

private void Smoothing(string origin)

{

this.picture = pbs[(int)EffectType.EF_SMOOTH].Effect(origin);

}

 

private void PreventRedEye(string origin)

{

this.picture = pbs[(int)EffectType.EF_PREVENTRE].Effect(origin);

}

 

private void EffectNormal(string origin)

{

this.picture = new Pictrure(origin);

}

}

}

 

그리고, 정상적으로 구현된 것을 확인하기 위한 코드는 진입점인 main에 구현하기로 하겠습니다. 테스트는 카메라에 적목 현상이 있는 눈과 날카로운 몸체를 피사체로 전달하여 사진을 얻어오는 것으로 하겠습니다. 물론, 여러 효과를 주어 결과 사진을 확인 해야겠습니다.

 

namespace AboutBuilderPattern

{

class Program

{

static void Main(string[] args)

{

Camera camera = new Camera();

camera.PressAShutter("RedEyeSharpBody", EffectType.EF_NORMAL);

Console.WriteLine(camera.GetPicture());

 

camera.PressAShutter("RedEyeSharpBody", EffectType.EF_PREVENTRE);

Console.WriteLine(camera.GetPicture());

 

camera.PressAShutter("RedEyeSharpBody", EffectType.EF_SMOOTH);

Console.WriteLine(camera.GetPicture());

 

camera.PressAShutter("RedEyeSharpBody", EffectType.EF_SMOOTH_PRERE);

Console.WriteLine(camera.GetPicture());

}

}

}

 

 

[그림 2] 빌더 패턴 예제 실행 화면

 

 

AboutBuilderPattern.zip

+ Recent posts