닷넷, C#, 디자인패턴, 팩토리패턴, Factory Pattern/Factory Method, Factory
Pattern/Factory Method
Factory 패턴은 오랜 기간동안 객체지향 시스템 디자이너들이 객체 생성에 대한 고민을 한 끝에 일반적인 시스템에서 객체 생성시에 자주 등장하는 디자인을 패턴화 시키고 이름은 붙여 놓은 것으로 이해하면 될 것이다. 따라서, 숙련된 시스템 디자이너에게는 Factory 패턴이 너무나 익숙한 내용일 것이고, 경험이 적은 개발자에게는 객체 생성에 대한 좋은 방법 하나를 익히는 기회가 될 것으로 생각한다.
2.1 객체생성의 두 가지 방법
여러분들이 객체지향 프로그래밍 언어를 가지고 필요한 객체를 생성하는 과정을 생각해 보라. 지금까지 자신이 객체를 생성하는데 사용한 과정이 크게 두 가지라는 것을 발견하게 될 것이다. 먼저 객체 생성의 주체가 되는 객체에서 다른 객체를 직접 생성하는 것이 하나요, 다른 하나는 객체 생성의 주체가 되는 객체가 생성하려는 객체를 직접 생성하는 것이 아니라 다른 객체에 해당 객체 생성을 의뢰하는 것이다.
두 가지 객체 생성 과정을 이해하기 위해서 "번역기" 시스템을 디자인 하여 보자. 번역기 시스템이란 사용자가 영어와 일어 두 가지 언어 중에서 하나를 선택하고 우리나라 문장을 넣으면 그 문장에 대한 해당 언어로 번역해 주는 시스템이다. 이 시스템을 위에서 구분 지은 객체 생성에 대한 두 가지 과정으로 나누어서 디자인하여 보고, 이 두 가지 디자인이 각각 어떤 차이를 보이는지 살펴보겠다.
2.2 직접 객체를 생성하는 경우
번역기를 만들기 위해서 그림 2와 같은 구조로 영어 번역기와 일본어 번역기를 디자인 하였다고 하자. 그림 2와 같은 디자인의 장점은 인터페이스(아래 첨부 글을 보기 바란다)를 이용하여 각 번역기를 사용하는 클라이언트와 각 번역기와의 의존성을 일부분 제거했다는 것이다.
<
그림 2. Translator 인터페이스를 이용한 번역 객체의 이용
그림 2의 디자인을 토대로 번역기 시스템의 클라이언트 부분의 구현을 한다면 소스 1과 같은 코드가 될 것이다.
public class TranslatorClient {
…
private Translator tr_;
if(language == ENGLISH) {
tr_ = new EnglishTranslator();
} else if (language == JAPANESE) {
tr_ = new JapaneseTranslator();
}
…
translatedSentence = tr_.translate(inputSentence);
…
}
소스 1. 그림 1의 구현 소스
번역기를 위한 그림 2와 같은 디자인은 그런대로 쓸만하다. 그 이유는 이미 이야기 했듯이 인터페이스를 클라이언트와 각각의 번역기 사이에 두어서 소스 1 중에서
translatedSentence = tr_.translate(inputSentence);
처럼 사용되는 번역기에 상관없이 같은 메소드를 호출한다는 것이다. 따라서, 이 경우 해당 번역기의 소스가 수정되더라도 클라이언트의 메소드 호출부분에 대한 소스는 고칠 곳이 없다. 그러나, 이 디자인에는 한 가지 아쉬운 부분이 남아 있다. 바로 해당 번역기를 생성하는 부분이다.
그림 2와 같은 디자인으로는 필요한 번역기의 객체를 생성하기 위해서 그 해당 번역기의 타입을 알아야 하는 문제점이 남아 있다.
즉, 소스 1에서
if(language == ENGLISH) {
tr_ = new EnglishTranslator();
} else if (language == JAPANESE) {
tr_ = new JapaneseTranslator();
}
처럼 필요한 번역기의 객체를 생성시키려면 굵게 표시된 부분처럼 구체적인 번역기의 타입을 알아야 한다. 이 부분이 발생시키는 문제점은 객체 생성에 있어서, 클라이언트와 번역기 사이의 의존성을 제거하지 못했다는데 있다. 다시 말해서, 지금은 영어 번역을 위해서 사용하는 객체가 EnglishTranslator이지만, 나중에 시스템을 업그레이드 할 경우 다른 타입의 번역기를 사용하게 되면 이 부분을 바꾸어 주어야 하는 것이다. 직접 해당 객체를 생성할 때 발생하는 이러한 의존성 문제를 해결하기 위해서 객체 생성을 다른 객체에 의뢰하는 디자인을 생각하게 된 것이다. 이제 그 이야기를 할 차례다.
2.3 다른 객체에 필요한 객체의 생성을 의뢰하는 경우
위에서 지적한대로 필요한 객체를 생성하는 곳에서 발생하는 객체 사이의 의존성을 없애기 위해서 생성에 대한 부분을 따로 떼내어 생성만 전담하는 객체를 하나 더 만드는 방법을 사용한다. 그림 3은 객체 생성에 따른 의존성 문제를 해결한 디자인이다.
<
그림 3. 객체 생성을 다른 객체에 의뢰하는 경우의 디자인 - Factory Method
그림 2의 디자인과 그림 3의 디자인을 비교해 보면, 달라진 부분이 하나 있다. 객체 생성을 전담하는 TranslatorFactoryImpl 객체를 하나 만들어 클라이언트가 Translator를 통해서 하던 번역기 객체 생성 일을 맡겼다는 것이다. 개선된 번역기 시스템의 디자인에 대한 클라이언트 부분의 소스 코드는 소스 2와 같이 수정이 가해질 것이다.
public class TranslatorClient {
…
private TranslatorFactory tf_;
private Translator tr_;
…
tf_ = new TranslatorFactoryImpl();
if(language == ENGLISH) {
tr_ = tf_.getEnglishTranslator();
} else if (language == JAPANESE) {
tr_ = tf_.getJapaneseTranslator();
}
…
translatedSentence = tr_.translate(inputSentence);
…
}
소스 2. 개선된 번역기 시스템의 클라이언트 소스
소스 2를 보면서 클라이언트 구현부가 더 복잡해 졌다고 투덜되는 독자도 있을 것이다. 그러나, 잘 뜯어 보면 이제는 객체 생성 부분에 남아 있던 의존성 부분까지도 제거 했음을 볼 수 있다. 객체 생성 부분에 남아 있던 의존성 부분이 제거된 부분은 소스 2에서 다음 부분이다.
if(language == ENGLISH) {
tr_ = tf_.getEnglishTranslator();
} else if (language == JAPANESE) {
tr_ = tf_.getJapaneseTranslator();
}
위에서 굵은 글씨로 되어 있는 부분이 예전에는 해당 번역기 객체의 타입이 명시되어있던 부분이다. 이 부분이 이제는 생성 책임을 맡고 있는 객체의 인터페이스로 바뀜으로 해서 번역기가 바뀌는 것에 대한 의존성을 완전히 제거되었다. 즉, 번역기 시스템은 기본적으로 번역을 하는 시스템이고 따라서 TranslatorFactory, Translator 인터페이스를 통해서 정의된 기능에 대한 수정이 가해지는 일은 거의 없을 것이다. 업그레이드를 위해서 수정이 가해지는 일이 발생한다면, 그것은 번역 정도의 향상을 위한 알고리즘의 변화나 번역기 교체에 대한 것이므로 그림 3과 같은 디자인으로 번역기 타입과 해당 번역기의 메소드 사용에 대한 의존성을 모두 없앤다면 유지, 보수가 매우 높은 시스템이 될 것이다.
앞에서도 이야기했지만 그림 3과 같은 시스템 디자인은 객체의 생성 문제를 효과적으로 해결할 수 있는 구조여서 경험이 많은 개발자들은 알게 모르게 이러한 디자인을 자신이 개발하고 있는 시스템에 사용하여 왔다. 이런 경험에 이름을 붙여서 정리해 놓은 것인 Factory Method다. GoF 책에 기술되어 있는 이 패턴의 의도는 다음과 같으며, 이 패턴 구조를 일반화 시켜서 그림 4와 같이 정리하였다
의도(Intent)
Define an interface for creating an object, but let subclasses decide which
class to instantiate. Factory Method lets a class defer instantiation to
subclasses.
객체 생성에 대한 인터페이스를 정의한다. 그러나, 생성할 객체에 대한 클래스의 선택은 이 인터페이스를 구현(Implementation)한 클래스가 결정하도록 한다. Factory Method는 객체 생성에 대한 책임을 인터페이스를 구현한 클래스에 전가한 것이다.[1]
객체 생성에 대한 인터페이스를 정의한다. 그러나, 생성할 객체에 대한 클래스의 선택은 이 인터페이스를 구현(Implementation)한 클래스가 결정하도록 한다. Factory Method는 객체 생성에 대한 책임을 인터페이스를 구현한 클래스에 전가한 것이다.[1]
<
>
오라클자바커뮤니티 실전 강좌 - 개인80% 환급
댓글 없음:
댓글 쓰기