brunch

제3장: 추상 팩토리 패턴

객체들의 조화

by jeromeNa

이케아 매장의 전시공간을 보다보면, 전시된 가구들의 조화로움에 나도 모르게 가구들을 하나하나 구매하게 됩니다. 북유럽 스타일 전시관은 심플하고 모던한 디자인의 의자, 테이블, 소파가 한 공간을 완벽하게 채우고 있죠. 빈티지 스타일은 클래식한 멋이 살아 있는 엔틱 가구들이 또 다른 분위기를 자아냅니다.


이는 1980년대 후반, GUI(그래픽 사용자 인터페이스) 시스템 개발에서 시작된 추상 팩토리 패턴의 철학과 맞닿아 있습니다. 당시 Richard Helm은 서로 다른 운영체제에서도 동일한 모양과 동작을 하는 UI 컴포넌트를 만들어야 하는 과제에 직면했습니다.


같은 시기에 일어난 Apple과 Microsoft 간의 GUI 저작권 소송은 이 문제의 중요성을 더욱 부각시켰습니다. Apple이 휴지통 아이콘이나 창 겹침과 같은 GUI 요소들에 대한 특허권을 주장하면서 시작된 이 소송은, 결국 스티브 잡스와 빌 게이츠의 합의로 마무리되었습니다.


이러한 배경 속에서 크로스 플랫폼 GUI 개발의 필요성이 대두되었습니다. 버튼, 스크롤바, 윈도우와 같은 요소들이 일관된 스타일을 유지하면서도 각 운영체제의 특성을 반영해야 했죠. 1994년 GoF의 책에서 소개된 추상 팩토리 패턴은 이 문제에 대한 우아한 해답이었습니다.


'추상(Abstract)'이라는 용어는 실제 구현이 아닌 개념적 수준에서의 정의를 의미합니다. 마치 "의자는 앉기 위한 것"이라는 개념적 정의처럼, 구체적인 형태가 아닌 본질적 기능을 정의하는 것입니다.


세계 여러 나라의 가구를 판매하는 가구점을 생각해 보겠습니다. 각 나라마다 의자, 테이블, 소파의 스타일이 다릅니다. 앞서 이야기한 북유럽은 심플하고 모던한 스타일, 빈티지한 엔틱 스타일, 우아한 프렌치 스타일.. 이처럼 하나의 테마로 연결된 제품군을 관리할 수 있어야 합니다.


추상 팩토리 패턴은 이러한 상황을 해결하기 위한 디자인 패턴입니다. 연관된 객체들을 일관된 방식으로 생성하는 방법을 제공합니다.


// 추상 제품들 - 각 가구 타입의 인터페이스
// 의자는 앉는 기능이 있어야 합니다.
interface Chair {
void sitOn();
}
// 테이블은 놓을 수 있어야 합니다.
interface Table {
void putOn();
}
// 소파는 누울수 있어야 합니다.
interface Sofa {
void lieOn();
}

// 구체적인 제품들 - 북유럽 스타일
// 북유럽풍의 의자를 만듭니다.
class NordicChair implements Chair {
public void sitOn() {
System.out.println("심플한 북유럽 의자에 앉습니다.");
}
}
// 북유럽풍의 테이블을 만듭니다.
class NordicTable implements Table {
public void putOn() {
System.out.println("모던한 북유럽 테이블에 물건을 올려놓습니다.");
}
}
// 북유럽풍의 소파를 만듭니다.
class NordicSofa implements Sofa {
public void lieOn() {
System.out.println("미니멀한 북유럽 소파에 눕습니다.");
}
}

// 구체적인 제품들 - 앤틱 스타일
class VintageChair implements Chair {
public void sitOn() {
System.out.println("클래식한 앤틱 의자에 앉습니다.");
}
}

// 추상 팩토리 - 가구 공장의 인터페이스
interface FurnitureFactory {
Chair createChair();
Table createTable();
Sofa createSofa();
}

// 구체적인 팩토리 - 북유럽 가구 공장에서 의자, 테이블, 소파를 만들어냅니다.
class NordicFurnitureFactory implements FurnitureFactory {
public Chair createChair() {
return new NordicChair();
}

public Table createTable() {
return new NordicTable();
}

public Sofa createSofa() {
return new NordicSofa();
}
}

위의 코드를 실제 사용하면 다음과 같습니다.

// 가구 매장에서 공장의 가구를 가져와 주문합니다.
public class FurnitureStore {
private FurnitureFactory factory;

public FurnitureStore(FurnitureFactory factory) {
this.factory = factory;
}

public void orderFurnitureSet() {
Chair chair = factory.createChair();
Table table = factory.createTable();
Sofa sofa = factory.createSofa();

System.out.println("가구 세트 주문:");
chair.sitOn();
table.putOn();
sofa.lieOn();
}
}

// 사용 예
FurnitureStore store = new FurnitureStore(new NordicFurnitureFactory());
store.orderFurnitureSet();


위의 코드 예시처럼 가구들을 추상적으로 정의 합니다. 의자는 앉아야 하고, 테이블은 놓아야 하며, 소파는 누워야 한다는 추상화를 합니다. 개념적으로 추상화된 가구 객체들을 북유럽풍, 엔틱풍 등으로 재정의해서 특색에 맞게 제작합니다.


팩토리(공장)에서는 각 스타일에 맞는 객체(가구)를 만들고, 매장에서 전시합니다. 주문이 이루어졌을 때 orderFunitureSet만 실행하면 원하는 스타일의 가구를 제작할 수 있는 구조입니다.


이 패턴은 다양한 분야에서 활용됩니다. UI 컴포넌트 생성, 자동차 부품 제작, 게임 캐릭터와 아이템 생성 등, 연관된 객체들의 일관성이 중요한 곳이라면 어디든 적용할 수 있습니다.


다만 새로운 제품을 추가하거나 기존 제품을 변경할 때는 신중해야 합니다. 하나의 변경이 전체 시스템에 영향을 미칠 수 있기 때문입니다. 이는 마치 가구점에서 새로운 스타일의 제품군을 도입할 때, 전체적인 조화를 고려해야 하는 것과 같습니다.


앞 장의 팩토리 메서드 패턴이 단일 제품 생산에 집중한다면, 추상 팩토리는 제품군 전체의 조화로운 생산을 추구합니다. 다소 복잡하지만 그만큼 강력한 패턴입니다. 특히 시스템이 여러 제품군을 다뤄야 하고, 제품군 간의 일관성이 중요할 때 유용합니다. 이는 마치 다양한 스타일의 가구를 체계적으로 관리하는 가구점과 같이, 연관된 객체들을 일관성 있게 생성하고 관리할 수 있게 해줍니다.


이처럼 추상 팩토리 패턴은 ‘일관성’‘조화’가 있습니다. 각각의 제품은 독립적이지만, 전체적으로는 하나의 통일된 테마를 가집니다.




keyword