Abstract Factory Pattern

Abstract Factory Pattern 는 관련 객체들의 계열 (Product Family) 생성을 추상화하는 패턴으로, 클라이언트는 구체 클래스에 의존하지 않고 팩토리 인터페이스에만 의존해 객체를 생성한다. Abstract Factory 와 Concrete Factory, Abstract Product 와 Concrete Product, Client 로 구성되며, 클라이언트는 Abstract Factory 에 팩토리 구현체를 주입받아 원하는 제품 계열을 생성할 수 있다. 이는 제품 간 일관성 유지, 다양한 제품군 전환, 테스트 유연성 향상 등에 강점을 제공한다.

핵심 개념

Abstract Factory Pattern(추상 팩토리 패턴) 은 관련된 객체들의 집합을 구체 클래스를 지정하지 않고 생성할 수 있는 인터페이스를 제공하는 생성 패턴이다.

기본 개념

  1. 추상 팩토리 (Abstract Factory): 관련 객체들의 패밀리를 생성하는 인터페이스
  2. 구체 팩토리 (Concrete Factory): 특정 제품군을 생성하는 구현체
  3. 추상 제품 (Abstract Product): 제품들의 공통 인터페이스
  4. 구체 제품 (Concrete Product): 실제 생성되는 객체들
  5. 제품군 (Product Family): 함께 사용되도록 설계된 관련 객체들의 집합
  6. 클라이언트 (Client): 팩토리를 통해 객체를 사용하는 코드
  7. 클라이언트 분리: 구체적인 구현부로부터 독립적인 클라이언트 코드

심화 개념

의도 (Intent)

구체 클래스를 지정하지 않고, 관련된 객체들의 집합 (제품군) 을 생성할 수 있는 인터페이스를 제공하되, 구체적인 클래스는 명시하지 않는다.

다른 이름 (Also Known As)

동기 (Motivation / Forces)

적용 가능성 (Applicability)

  1. 시스템 독립성: 객체 생성, 구성, 표현 방식과 무관한 시스템 구축
  2. 제품군 선택: 여러 제품군 중 하나를 선택하여 시스템 구성
  3. 제품 일관성: 관련 제품들이 함께 사용되도록 제약 적용
  4. 라이브러리 제공: 구현이 아닌 인터페이스만 노출하는 라이브러리

패턴이 해결하고자 하는 설계 문제

  1. 플랫폼 종속성: 특정 플랫폼에 종속된 객체 생성 로직
  2. 제품군 불일치: 서로 다른 제품군의 객체들이 혼재하여 사용되는 문제
  3. 확장성 부족: 새로운 제품군 추가 시 기존 코드의 광범위한 수정
  4. 객체 생성 복잡성: 관련 객체들의 복잡한 생성 과정과 의존성

협력 (Collaboration)

실무 구현 연관성

핵심 개념들이 실무 구현과 연결되는 측면:

배경

객체 지향 시스템에서 관련 객체들의 패밀리를 생성해야 할 때, 구체적인 클래스를 명시하면 코드가 환경에 종속되고 확장성이 떨어짐. 이를 해결하기 위해 객체 생성 로직을 추상화하는 패턴이 필요해짐.

목적 및 필요성

주요 기능 및 역할

기능

  1. 객체 생성 캡슐화: 객체 생성 과정을 캡슐화하여 클라이언트로부터 숨김
  2. 제품군 일관성 보장: 동일한 제품군 내의 객체들만 함께 사용되도록 보장
  3. 런타임 제품군 교체: 실행 중에 사용할 제품군을 변경 가능
  4. 확장성 제공: 새로운 제품군 추가 시 기존 코드 수정 최소화

역할

특징

핵심 원칙

  1. 추상화 원칙: 구체적인 구현보다는 추상 인터페이스에 의존
  2. 캡슐화 원칙: 객체 생성 과정을 팩토리 내부에 숨김
  3. 일관성 원칙: 동일한 제품군 내의 객체들만 함께 사용
  4. 확장성 원칙: 기존 코드 수정 없이 새로운 기능 추가 가능

SOLID 원칙의 관계

원칙적용 예시
SRP (Single Responsibility Principle)각 팩토리는 생성 책임만 수행
OCP (Open/Closed Principle)새로운 팩토리 추가 시 기존 클라이언트 변경 불필요
DIP (Dependency Inversion Principle)클라이언트는 추상 팩토리 인터페이스에 의존
ISP (Interface Segregation Principle)팩토리 인터페이스는 제품군 단위로 분리 가능
LSP (Liskov Substitution Principle)추상 팩토리 대체에 있어 기능 보장 가능

주요 원리

  1. 클라이언트는 AbstractFactory 인터페이스를 통해 객체 생성
  2. 구체 팩토리 (ConcreteFactory) 는 제품군별 객체를 생성해 반환
  3. 클라이언트는 추상 제품 타입만 사용하며, 구체 클래스는 몰라도 됨
  4. 제품군 교체 시 ConcreteFactory 만 교체하면 됨

참여자들 간의 상호작용 방식

graph LR
    A[Client Request] --> B{Factory Selection}
    B --> C[Windows Factory]
    B --> D[macOS Factory]
    B --> E[Linux Factory]
    C --> F[Windows Products]
    D --> G[macOS Products]
    E --> H[Linux Products]
    F --> I[Client Usage]
    G --> I
    H --> I
    I --> J[Consistent Interface]

상호작용 패턴:

  1. 초기화 단계: 환경 감지 후 적절한 팩토리 선택
  2. 생성 단계: 팩토리를 통한 관련 제품들의 일괄 생성
  3. 사용 단계: 추상 인터페이스를 통한 제품 조작
  4. 교체 단계: 필요시 다른 팩토리로 전환

문제를 해결하기 위한 설계 구조와 관계

sequenceDiagram
    participant Client
    participant AbstractFactory
    participant ConcreteFactory
    participant AbstractProduct
    participant ConcreteProduct

    Client->>AbstractFactory: getFactory()
    AbstractFactory-->>ConcreteFactory: returns instance
    Client->>ConcreteFactory: createProduct()
    ConcreteFactory-->>ConcreteProduct: instantiate
    ConcreteProduct-->>Client: returns as AbstractProduct
    Client->>AbstractProduct: use()

설계 구조의 핵심 관계:

작동 메커니즘

sequenceDiagram
    participant Client
    participant AbstractFactory
    participant ConcreteFactoryA
    participant ConcreteProductA1
    participant ConcreteProductA2
    participant ConcreteFactoryB
    participant ConcreteProductB1
    participant ConcreteProductB2
    
    Note over Client: 팩토리 선택 및 초기화
    Client->>ConcreteFactoryA: new ConcreteFactoryA()
    ConcreteFactoryA-->>Client: ConcreteFactoryA 인스턴스
    
    Note over Client: 제품군 A의 제품들 생성
    Client->>ConcreteFactoryA: createProductType1()
    ConcreteFactoryA->>ConcreteProductA1: new ConcreteProductA1()
    ConcreteProductA1-->>ConcreteFactoryA: ConcreteProductA1 인스턴스
    ConcreteFactoryA-->>Client: ProductType1 인터페이스로 반환
    
    Client->>ConcreteFactoryA: createProductType2()
    ConcreteFactoryA->>ConcreteProductA2: new ConcreteProductA2()
    ConcreteProductA2-->>ConcreteFactoryA: ConcreteProductA2 인스턴스
    ConcreteFactoryA-->>Client: ProductType2 인터페이스로 반환
    
    Note over Client: 다른 제품군으로 전환
    Client->>ConcreteFactoryB: new ConcreteFactoryB()
    ConcreteFactoryB-->>Client: ConcreteFactoryB 인스턴스
    
    Note over Client: 제품군 B의 제품들 생성
    Client->>ConcreteFactoryB: createProductType1()
    ConcreteFactoryB->>ConcreteProductB1: new ConcreteProductB1()
    ConcreteProductB1-->>ConcreteFactoryB: ConcreteProductB1 인스턴스
    ConcreteFactoryB-->>Client: ProductType1 인터페이스로 반환
    
    Client->>ConcreteFactoryB: createProductType2()
    ConcreteFactoryB->>ConcreteProductB2: new ConcreteProductB2()
    ConcreteProductB2-->>ConcreteFactoryB: ConcreteProductB2 인스턴스
    ConcreteFactoryB-->>Client: ProductType2 인터페이스로 반환

패턴 적용의 결과와 트레이드오프

긍정적 결과
트레이드오프

패턴에 참여하는 클래스와 객체들

구성 요소역할책임특징
AbstractFactory제품 생성 메서드들의 인터페이스 정의제품군 생성의 계약 명시제품군별 생성 메서드를 순수 가상 함수로 정의
ConcreteFactory특정 제품군의 실제 생성 구현관련 제품들 (Button, Checkbox 등) 의 일관된 생성하나의 제품군에 특화되어 동작
AbstractProduct제품의 공통 인터페이스 정의제품 간의 표준 동작 정의제품 타입별 (Button, Checkbox 등) 로 별도 정의
ConcreteProduct실제 제품의 구현추상 제품 인터페이스의 기능 구현, 플랫폼별 차별화 제공특정 팩토리에서만 생성되며, 기능은 추상 제품을 기반으로 확장
Client추상 인터페이스를 통해 제품을 사용팩토리를 이용해 제품을 생성하고 비즈니스 로직 수행구체적 팩토리/제품에 대한 의존 없이 추상화에만 의존

구현 기법

구현 기법정의구성 요소목적실제 예시
** 기본 구현 기법**인터페이스 기반의 고정된 팩토리 클래스와 제품군 구조추상 팩토리, 구체 팩토리, 제품 인터페이스, 구체 제품제품군 간 일관된 생성 구조 제공GUI 위젯 (MacButton, WinButton)
** 팩토리 등록 기법**팩토리를 레지스트리에 등록하고, 조건에 따라 런타임에 팩토리를 선택팩토리 맵, 등록 API, 조건 분기 로직플러그인 구조, 런타임 확장성JDBC 드라이버, 브라우저 플러그인
** 파라미터화된 팩토리**파라미터 (키) 를 받아 하나의 팩토리 메서드에서 다양한 제품을 생성조건문/딕셔너리, 제품 식별자, 단일 팩토리 메서드코드 수 감소, 분기 로직 통합몬스터 생성 팩토리, DB 커넥션 팩토리
** 싱글톤 팩토리**팩토리를 애플리케이션 전역에서 하나만 생성하고 공유하는 구조정적 인스턴스, getInstance(), 비공개 생성자전역 상태 공유, 메모리 절감LoggerFactory, ConfigFactory
** Reflection 기반 기법**클래스명을 문자열로 받아 동적으로 팩토리 또는 제품 객체를 생성클래스 매핑, getattr() 또는 __import__()동적 생성, 제품 추가 시 코드 수정 최소화Python 모듈 로딩, 자바의 Class.forName
** 프로토타입 기반 기법**초기 템플릿 객체를 복제하여 새 객체 생성 (복잡한 객체 복사에 적합)원형 객체, copy() 혹은 .clone() 메서드복잡한 객체 복사 비용 절감게임 캐릭터, UI 템플릿 복사
DSL 기반 팩토리선언적 구성 (DSL) 으로 팩토리 로직 정의 후 런타임에 파싱하여 객체 생성구성 파일 (JSON/YAML), 파서, 런타임 생성기설정 기반 확장 가능, UI/워크플로우 제너레이터템플릿 엔진, UI DSL (Flutter, Unity 등)

기본 구현 기법 (Standard)

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
# Abstract Factory
class UIFactory:
    def create_button(self): pass
    def create_checkbox(self): pass

# Concrete Factory
class MacUIFactory(UIFactory):
    def create_button(self): return MacButton()
    def create_checkbox(self): return MacCheckbox()

# Abstract Products
class Button: pass
class Checkbox: pass

# Concrete Products
class MacButton(Button):
    def render(self): print("Mac Button")

class MacCheckbox(Checkbox):
    def render(self): print("Mac Checkbox")

# Client
factory = MacUIFactory()
btn = factory.create_button()
btn.render()

팩토리 등록 기법 (Registry)

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
factory_registry = {}

def register_factory(name, factory_cls):
    factory_registry[name] = factory_cls

def get_factory(name):
    return factory_registry[name]()

# 등록
register_factory("mac", MacUIFactory)
register_factory("win", lambda: WindowsUIFactory())

# 사용
factory = get_factory("mac")
btn = factory.create_button()
btn.render()

파라미터화된 팩토리

1
2
3
4
5
6
7
8
9
class UIFactory:
    def create_component(self, component_type):
        if component_type == "button":
            return MacButton()
        elif component_type == "checkbox":
            return MacCheckbox()

factory = UIFactory()
factory.create_component("button").render()

싱글톤 팩토리

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
class ConfigFactory:
    _instance = None

    def __new__(cls):
        if cls._instance is None:
            cls._instance = super().__new__(cls)
            cls._instance.config = {"env": "production"}
        return cls._instance

config1 = ConfigFactory()
config2 = ConfigFactory()
assert config1 is config2  # 동일 인스턴스

리플렉션 기반

1
2
3
4
5
6
7
8
9
class MacButton: pass
class WinButton: pass

def create_button(class_name: str):
    cls = globals()[class_name]
    return cls()

btn = create_button("MacButton")
print(type(btn))

프로토타입 기반 기법

1
2
3
4
5
6
7
8
9
import copy

class Monster:
    def __init__(self, name): self.name = name
    def clone(self): return copy.deepcopy(self)

base_monster = Monster("Orc")
monster1 = base_monster.clone()
print(monster1.name)

DSL 기반 팩토리

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
import json

# config.json = {"ui_type": "mac"}
config = json.loads('{"ui_type": "mac"}')

def factory_from_config(conf):
    if conf["ui_type"] == "mac":
        return MacUIFactory()
    elif conf["ui_type"] == "win":
        return WindowsUIFactory()

factory = factory_from_config(config)
factory.create_button().render()

열거형 기반 팩토리 선택

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
from enum import Enum

class PlatformType(Enum):
    WINDOWS = "windows"
    MACOS = "macos"
    LINUX = "linux"

class FactoryRegistry:
    _factories = {}
    
    @classmethod
    def register(cls, platform: PlatformType, factory: UIComponentFactory):
        cls._factories[platform] = factory
    
    @classmethod
    def get_factory(cls, platform: PlatformType) -> UIComponentFactory:
        return cls._factories.get(platform)

# Registration
FactoryRegistry.register(PlatformType.WINDOWS, WindowsUIFactory())
FactoryRegistry.register(PlatformType.MACOS, MacOSUIFactory())

설정 기반 팩토리 로딩

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
import json
from typing import Dict, Any

class ConfigurableFactory:
    def __init__(self, config_file: str):
        with open(config_file, 'r') as f:
            self.config: Dict[str, Any] = json.load(f)
    
    def create_factory(self) -> UIComponentFactory:
        factory_type = self.config.get('ui_factory_type', 'default')
        
        factory_map = {
            'windows': WindowsUIFactory,
            'macos': MacOSUIFactory,
            'cross_platform': CrossPlatformUIFactory
        }
        
        factory_class = factory_map.get(factory_type, WindowsUIFactory)
        return factory_class()

유사하거나 관련된 패턴들

구분패턴관계 타입설명주요 차이점조합 시나리오
객체 생성Factory Method협력/구성 관계Abstract Factory 의 구성 요소로 활용단일 제품 vs 제품군 생성Abstract Factory 내부에서 Factory Method 패턴 사용
객체 생성/조립Builder협력/조합 관계복잡한 제품 생성 시 함께 사용제품군 선택 vs 제품 조립GUI 프레임워크에서 컴포넌트 생성 및 조립
객체 생성/관리Prototype협력/대체 관계팩토리 내부에서 복제 기반 생성새 인스턴스 vs 복제 기반 생성비용이 큰 객체의 효율적 생성
객체 생명주기Singleton협력/보완 관계팩토리 인스턴스의 유일성 보장다중 인스턴스 vs 단일 인스턴스전역 팩토리 접근점 제공
인터페이스 단순화Facade대안/경쟁 관계간단한 객체 생성 시 대안복잡한 제품군 vs 단순 인터페이스서브시스템 객체 생성 단순화

Factory Method

Abstract Factory 는 여러 Factory Method 들의 집합으로 구현되며, 각 제품 생성 메서드가 Factory Method 패턴을 따른다.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
from abc import ABC, abstractmethod
from typing import List

# Abstract Products
class DatabaseConnection(ABC):
    @abstractmethod
    def connect(self) -> str:
        pass

class DatabaseQuery(ABC):
    @abstractmethod
    def execute(self, sql: str) -> str:
        pass

# Concrete Products - MySQL
class MySQLConnection(DatabaseConnection):
    def connect(self) -> str:
        return "Connected to MySQL database"

class MySQLQuery(DatabaseQuery):
    def execute(self, sql: str) -> str:
        return f"Executing MySQL query: {sql}"

# Concrete Products - PostgreSQL
class PostgreSQLConnection(DatabaseConnection):
    def connect(self) -> str:
        return "Connected to PostgreSQL database"

class PostgreSQLQuery(DatabaseQuery):
    def execute(self, sql: str) -> str:
        return f"Executing PostgreSQL query: {sql}"

# Abstract Factory using Factory Methods
class DatabaseFactory(ABC):
    # Factory Method for Connection
    @abstractmethod
    def create_connection(self) -> DatabaseConnection:
        pass
    
    # Factory Method for Query
    @abstractmethod
    def create_query(self) -> DatabaseQuery:
        pass
    
    # Template Method using Factory Methods
    def create_database_toolkit(self) -> dict:
        """템플릿 메서드로 팩토리 메서드들을 조합"""
        return {
            'connection': self.create_connection(),
            'query': self.create_query()
        }

# Concrete Factories implementing Factory Methods
class MySQLFactory(DatabaseFactory):
    def create_connection(self) -> DatabaseConnection:
        """Factory Method 구현"""
        print("Creating MySQL connection using Factory Method")
        return MySQLConnection()
    
    def create_query(self) -> DatabaseQuery:
        """Factory Method 구현"""
        print("Creating MySQL query handler using Factory Method")
        return MySQLQuery()

class PostgreSQLFactory(DatabaseFactory):
    def create_connection(self) -> DatabaseConnection:
        """Factory Method 구현"""
        print("Creating PostgreSQL connection using Factory Method")
        return PostgreSQLConnection()
    
    def create_query(self) -> DatabaseQuery:
        """Factory Method 구현"""
        print("Creating PostgreSQL query handler using Factory Method")
        return PostgreSQLQuery()

# Usage Example
def demonstrate_factory_method_relationship():
    print("=== Abstract Factory + Factory Method 협력 관계 ===")
    
    # MySQL 팩토리 사용
    mysql_factory = MySQLFactory()
    mysql_toolkit = mysql_factory.create_database_toolkit()
    
    print(mysql_toolkit['connection'].connect())
    print(mysql_toolkit['query'].execute("SELECT * FROM users"))
    
    print("\n" + "-"*50 + "\n")
    
    # PostgreSQL 팩토리 사용
    postgres_factory = PostgreSQLFactory()
    postgres_toolkit = postgres_factory.create_database_toolkit()
    
    print(postgres_toolkit['connection'].connect())
    print(postgres_toolkit['query'].execute("SELECT * FROM products"))

demonstrate_factory_method_relationship()

Prototype

Abstract Factory 에서 Prototype 을 사용하여 미리 구성된 객체들을 복제하는 방식으로 제품을 생성한다.

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
import copy
from abc import ABC, abstractmethod
from typing import Dict, Any

# Prototype Interface
class Prototype(ABC):
    @abstractmethod
    def clone(self) -> 'Prototype':
        pass

# Concrete Products implementing Prototype
class UITheme(Prototype):
    def __init__(self, name: str, primary_color: str, secondary_color: str, font_family: str):
        self.name = name
        self.primary_color = primary_color
        self.secondary_color = secondary_color
        self.font_family = font_family
        self.custom_properties: Dict[str, Any] = {}
    
    def clone(self) -> 'UITheme':
        """Deep copy를 통한 프로토타입 복제"""
        cloned = UITheme(
            self.name,
            self.primary_color,
            self.secondary_color,
            self.font_family
        )
        cloned.custom_properties = copy.deepcopy(self.custom_properties)
        return cloned
    
    def customize(self, **kwargs):
        """복제된 객체의 커스터마이징"""
        for key, value in kwargs.items():
            if hasattr(self, key):
                setattr(self, key, value)
            else:
                self.custom_properties[key] = value
    
    def __str__(self):
        return f"Theme: {self.name} ({self.primary_color}/{self.secondary_color})"

class UIComponent(Prototype):
    def __init__(self, component_type: str, width: int, height: int):
        self.component_type = component_type
        self.width = width
        self.height = height
        self.style_properties: Dict[str, Any] = {}
    
    def clone(self) -> 'UIComponent':
        """Shallow copy로 충분한 경우의 프로토타입 복제"""
        cloned = UIComponent(self.component_type, self.width, self.height)
        cloned.style_properties = copy.copy(self.style_properties)
        return cloned
    
    def apply_theme(self, theme: UITheme):
        """테마 적용"""
        self.style_properties.update({
            'primary_color': theme.primary_color,
            'secondary_color': theme.secondary_color,
            'font_family': theme.font_family
        })
    
    def __str__(self):
        return f"{self.component_type}: {self.width}x{self.height}"

# Prototype Registry
class PrototypeRegistry:
    def __init__(self):
        self._prototypes: Dict[str, Prototype] = {}
    
    def register(self, key: str, prototype: Prototype):
        """프로토타입 등록"""
        self._prototypes[key] = prototype
    
    def create(self, key: str) -> Prototype:
        """등록된 프로토타입으로부터 복제 생성"""
        prototype = self._prototypes.get(key)
        if prototype:
            return prototype.clone()
        raise ValueError(f"Prototype with key '{key}' not found")

# Abstract Factory using Prototypes
class UIComponentFactory(ABC):
    def __init__(self):
        self.theme_registry = PrototypeRegistry()
        self.component_registry = PrototypeRegistry()
        self._initialize_prototypes()
    
    @abstractmethod
    def _initialize_prototypes(self):
        """각 팩토리별 프로토타입 초기화"""
        pass
    
    def create_theme(self, theme_name: str = "default") -> UITheme:
        """프로토타입 기반 테마 생성"""
        return self.theme_registry.create(theme_name)
    
    def create_component(self, component_type: str) -> UIComponent:
        """프로토타입 기반 컴포넌트 생성"""
        return self.component_registry.create(component_type)
    
    def create_themed_component(self, component_type: str, theme_name: str = "default") -> UIComponent:
        """테마가 적용된 컴포넌트 생성"""
        component = self.create_component(component_type)
        theme = self.create_theme(theme_name)
        component.apply_theme(theme)
        return component

# Concrete Factory for Mobile UI
class MobileUIFactory(UIComponentFactory):
    def _initialize_prototypes(self):
        """모바일 특화 프로토타입들 등록"""
        
        # Mobile Themes
        mobile_light_theme = UITheme(
            "Mobile Light", "#007AFF", "#34C759", "SF Pro Display"
        )
        mobile_light_theme.custom_properties = {
            "corner_radius": 12,
            "shadow_enabled": True,
            "haptic_feedback": True
        }
        
        mobile_dark_theme = UITheme(
            "Mobile Dark", "#0A84FF", "#32D74B", "SF Pro Display"
        )
        mobile_dark_theme.custom_properties = {
            "corner_radius": 12,
            "shadow_enabled": False,
            "haptic_feedback": True
        }
        
        self.theme_registry.register("default", mobile_light_theme)
        self.theme_registry.register("light", mobile_light_theme)
        self.theme_registry.register("dark", mobile_dark_theme)
        
        # Mobile Components
        mobile_button = UIComponent("Mobile Button", 280, 44)
        mobile_button.style_properties = {
            "corner_radius": 12,
            "touch_target_size": 44,
            "animation_duration": 0.2
        }
        
        mobile_card = UIComponent("Mobile Card", 320, 120)
        mobile_card.style_properties = {
            "corner_radius": 16,
            "elevation": 2,
            "margin": 16
        }
        
        self.component_registry.register("button", mobile_button)
        self.component_registry.register("card", mobile_card)

# Concrete Factory for Desktop UI
class DesktopUIFactory(UIComponentFactory):
    def _initialize_prototypes(self):
        """데스크톱 특화 프로토타입들 등록"""
        
        # Desktop Themes
        desktop_light_theme = UITheme(
            "Desktop Light", "#0078D4", "#107C10", "Segoe UI"
        )
        desktop_light_theme.custom_properties = {
            "corner_radius": 4,
            "shadow_enabled": True,
            "hover_effects": True
        }
        
        desktop_dark_theme = UITheme(
            "Desktop Dark", "#4CC2FF", "#6CCB5F", "Segoe UI"
        )
        desktop_dark_theme.custom_properties = {
            "corner_radius": 4,
            "shadow_enabled": True,
            "hover_effects": True
        }
        
        self.theme_registry.register("default", desktop_light_theme)
        self.theme_registry.register("light", desktop_light_theme)
        self.theme_registry.register("dark", desktop_dark_theme)
        
        # Desktop Components
        desktop_button = UIComponent("Desktop Button", 120, 32)
        desktop_button.style_properties = {
            "corner_radius": 4,
            "hover_animation": True,
            "keyboard_navigation": True
        }
        
        desktop_card = UIComponent("Desktop Card", 400, 200)
        desktop_card.style_properties = {
            "corner_radius": 8,
            "shadow_depth": 4,
            "resize_handles": True
        }
        
        self.component_registry.register("button", desktop_button)
        self.component_registry.register("card", desktop_card)

# Client code demonstrating the pattern combination
class UIApplicationBuilder:
    def __init__(self, factory: UIComponentFactory):
        self.factory = factory
    
    def create_login_screen(self, theme: str = "default") -> Dict[str, UIComponent]:
        """로그인 화면 구성 요소들 생성"""
        components = {}
        
        # 프로토타입 기반으로 컴포넌트들 생성 및 커스터마이징
        login_button = self.factory.create_themed_component("button", theme)
        login_button.component_type = "Login Button"
        
        user_card = self.factory.create_themed_component("card", theme)
        user_card.component_type = "User Input Card"
        
        components['login_button'] = login_button
        components['user_card'] = user_card
        
        return components

# Usage Example
def demonstrate_prototype_relationship():
    print("=== Abstract Factory + Prototype 협력 관계 ===")
    
    # Mobile UI Factory with Prototypes
    mobile_factory = MobileUIFactory()
    mobile_builder = UIApplicationBuilder(mobile_factory)
    
    print("--- Mobile 로그인 화면 (Light Theme) ---")
    mobile_light_components = mobile_builder.create_login_screen("light")
    for name, component in mobile_light_components.items():
        print(f"{name}: {component}")
        print(f"  Style Properties: {component.style_properties}")
    
    print("\n--- Mobile 로그인 화면 (Dark Theme) ---")
    mobile_dark_components = mobile_builder.create_login_screen("dark")
    for name, component in mobile_dark_components.items():
        print(f"{name}: {component}")
        print(f"  Style Properties: {component.style_properties}")
    
    print("\n" + "-"*50 + "\n")
    
    # Desktop UI Factory with Prototypes
    desktop_factory = DesktopUIFactory()
    desktop_builder = UIApplicationBuilder(desktop_factory)
    
    print("--- Desktop 로그인 화면 (Light Theme) ---")
    desktop_components = desktop_builder.create_login_screen("light")
    for name, component in desktop_components.items():
        print(f"{name}: {component}")
        print(f"  Style Properties: {component.style_properties}")
    
    # Prototype의 장점 - 성능 테스트
    print("\n--- 프로토타입 성능 비교 ---")
    import time
    
    # 프로토타입 기반 생성 (빠름)
    start_time = time.time()
    for _ in range(1000):
        mobile_factory.create_component("button")
    prototype_time = time.time() - start_time
    
    print(f"프로토타입 기반 1000개 버튼 생성: {prototype_time:f}초")
    
    # 직접 생성과 비교해보기 위한 데모
    start_time = time.time()
    for _ in range(1000):
        component = UIComponent("Mobile Button", 280, 44)
        component.style_properties = {
            "corner_radius": 12,
            "touch_target_size": 44,
            "animation_duration": 0.2
        }
    direct_time = time.time() - start_time
    
    print(f"직접 생성 1000개 버튼 생성: {direct_time:f}초")
    print(f"성능 개선: {((direct_time - prototype_time) / direct_time * 100):f}%")

demonstrate_prototype_relationship()

Singleton

구체적 팩토리 인스턴스는 보통 싱글톤으로 구현되어 시스템 전체에서 하나의 팩토리만 사용하도록 보장한다.

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
import threading
from abc import ABC, abstractmethod
from typing import Dict, Optional, Type

# Thread-safe Singleton Metaclass
class SingletonMeta(type):
    """스레드 안전한 싱글톤 메타클래스"""
    _instances: Dict[Type, object] = {}
    _lock: threading.Lock = threading.Lock()
    
    def __call__(cls, *args, **kwargs):
        if cls not in cls._instances:
            with cls._lock:
                # Double-checked locking pattern
                if cls not in cls._instances:
                    instance = super().__call__(*args, **kwargs)
                    cls._instances[cls] = instance
        return cls._instances[cls]

# Products
class Logger(ABC):
    @abstractmethod
    def log(self, message: str, level: str = "INFO") -> None:
        pass

class ConfigManager(ABC):
    @abstractmethod
    def get_config(self, key: str) -> Optional[str]:
        pass
    
    @abstractmethod
    def set_config(self, key: str, value: str) -> None:
        pass

# Concrete Products
class FileLogger(Logger):
    def __init__(self, filename: str):
        self.filename = filename
        self.file_handle = None
    
    def log(self, message: str, level: str = "INFO") -> None:
        log_entry = f"[{level}] {message}"
        print(f"File Logger ({self.filename}): {log_entry}")

class DatabaseLogger(Logger):
    def __init__(self, connection_string: str):
        self.connection_string = connection_string
    
    def log(self, message: str, level: str = "INFO") -> None:
        log_entry = f"[{level}] {message}"
        print(f"Database Logger ({self.connection_string}): {log_entry}")

class JSONConfigManager(ConfigManager):
    def __init__(self, config_file: str):
        self.config_file = config_file
        self.config_data: Dict[str, str] = {}
    
    def get_config(self, key: str) -> Optional[str]:
        return self.config_data.get(key)
    
    def set_config(self, key: str, value: str) -> None:
        self.config_data[key] = value
        print(f"JSON Config: Set {key} = {value}")

class DatabaseConfigManager(ConfigManager):
    def __init__(self, connection_string: str):
        self.connection_string = connection_string
        self.config_cache: Dict[str, str] = {}
    
    def get_config(self, key: str) -> Optional[str]:
        return self.config_cache.get(key)
    
    def set_config(self, key: str, value: str) -> None:
        self.config_cache[key] = value
        print(f"Database Config ({self.connection_string}): Set {key} = {value}")

# Abstract Factory
class SystemComponentFactory(ABC):
    @abstractmethod
    def create_logger(self) -> Logger:
        pass
    
    @abstractmethod
    def create_config_manager(self) -> ConfigManager:
        pass
    
    def create_system_bundle(self) -> Dict[str, object]:
        """시스템 구성 요소들을 묶어서 반환"""
        return {
            'logger': self.create_logger(),
            'config_manager': self.create_config_manager()
        }

# Singleton Concrete Factories
class DevelopmentSystemFactory(SystemComponentFactory, metaclass=SingletonMeta):
    """개발 환경용 싱글톤 팩토리"""
    
    def __init__(self):
        if hasattr(self, '_initialized'):
            return
        
        self._initialized = True
        self._logger_instance: Optional[Logger] = None
        self._config_manager_instance: Optional[ConfigManager] = None
        print("Development System Factory initialized (Singleton)")
    
    def create_logger(self) -> Logger:
        """싱글톤 팩토리가 동일한 로거 인스턴스 반환"""
        if self._logger_instance is None:
            self._logger_instance = FileLogger("dev.log")
            print("Created new FileLogger for development")
        return self._logger_instance
    
    def create_config_manager(self) -> ConfigManager:
        """싱글톤 팩토리가 동일한 설정 관리자 인스턴스 반환"""
        if self._config_manager_instance is None:
            self._config_manager_instance = JSONConfigManager("dev_config.json")
            print("Created new JSONConfigManager for development")
        return self._config_manager_instance

class ProductionSystemFactory(SystemComponentFactory, metaclass=SingletonMeta):
    """운영 환경용 싱글톤 팩토리"""
    
    def __init__(self):
        if hasattr(self, '_initialized'):
            return
        
        self._initialized = True
        self._logger_instance: Optional[Logger] = None
        self._config_manager_instance: Optional[ConfigManager] = None
        print("Production System Factory initialized (Singleton)")
    
    def create_logger(self) -> Logger:
        if self._logger_instance is None:
            self._logger_instance = DatabaseLogger("prod://logging-db:5432")
            print("Created new DatabaseLogger for production")
        return self._logger_instance
    
    def create_config_manager(self) -> ConfigManager:
        if self._config_manager_instance is None:
            self._config_manager_instance = DatabaseConfigManager("prod://config-db:5432")
            print("Created new DatabaseConfigManager for production")
        return self._config_manager_instance

# Factory Provider (also Singleton)
class SystemFactoryProvider(metaclass=SingletonMeta):
    """시스템 팩토리 제공자 - 환경별 적절한 팩토리 반환"""
    
    def __init__(self):
        if hasattr(self, '_initialized'):
            return
        
        self._initialized = True
        self._current_environment: str = "development"
        self._factories: Dict[str, Type[SystemComponentFactory]] = {
            "development": DevelopmentSystemFactory,
            "production": ProductionSystemFactory
        }
        print("System Factory Provider initialized (Singleton)")
    
    def set_environment(self, environment: str) -> None:
        """환경 설정 변경"""
        if environment in self._factories:
            self._current_environment = environment
            print(f"Environment switched to: {environment}")
        else:
            raise ValueError(f"Unknown environment: {environment}")
    
    def get_factory(self) -> SystemComponentFactory:
        """현재 환경에 맞는 팩토리 반환 (싱글톤)"""
        factory_class = self._factories[self._current_environment]
        return factory_class()  # 메타클래스에 의해 싱글톤 보장

# Application Service using the pattern
class ApplicationService:
    def __init__(self):
        self.factory_provider = SystemFactoryProvider()
        self.system_components: Optional[Dict[str, object]] = None
    
    def initialize_system(self, environment: str = "development"):
        """시스템 초기화"""
        self.factory_provider.set_environment(environment)
        factory = self.factory_provider.get_factory()
        self.system_components = factory.create_system_bundle()
        
        # 초기 설정
        config_manager = self.system_components['config_manager']
        config_manager.set_config("app_name", "MyApplication")
        config_manager.set_config("version", "1.0.0")
        
        # 초기 로그
        logger = self.system_components['logger']
        logger.log(f"System initialized in {environment} mode", "INFO")
    
    def perform_operation(self, operation_name: str):
        """비즈니스 로직 수행"""
        if not self.system_components:
            raise RuntimeError("System not initialized")
        
        logger = self.system_components['logger']
        config_manager = self.system_components['config_manager']
        
        app_name = config_manager.get_config("app_name")
        logger.log(f"{app_name}: Performing {operation_name}", "INFO")

# Multi-threaded usage demonstration
def worker_thread(thread_id: int, environment: str):
    """스레드별 작업 시뮬레이션"""
    print(f"\n--- Thread {thread_id} started ---")
    
    service = ApplicationService()
    service.initialize_system(environment)
    service.perform_operation(f"Task-{thread_id}")
    
    # 팩토리 인스턴스 확인
    provider = SystemFactoryProvider()
    factory = provider.get_factory()
    print(f"Thread {thread_id}: Factory instance ID = {id(factory)}")
    
    print(f"--- Thread {thread_id} completed ---")

# Usage Example
def demonstrate_singleton_relationship():
    print("=== Abstract Factory + Singleton 협력 관계 ===")
    
    # Single-threaded usage
    print("--- 단일 스레드 사용 ---")
    service1 = ApplicationService()
    service1.initialize_system("development")
    service1.perform_operation("User Registration")
    
    # 동일한 팩토리 인스턴스 사용 확인
    service2 = ApplicationService()
    service2.initialize_system("development")
    service2.perform_operation("User Login")
    
    # 팩토리 인스턴스 비교
    provider1 = SystemFactoryProvider()
    provider2 = SystemFactoryProvider()
    print(f"\nProvider instances are same: {id(provider1) == id(provider2)}")
    
    factory1 = provider1.get_factory()
    factory2 = provider2.get_factory()
    print(f"Factory instances are same: {id(factory1) == id(factory2)}")
    
    print("\n" + "-"*50 + "\n")
    
    # Multi-threaded usage
    print("--- 멀티 스레드 사용 (Singleton 스레드 안전성 테스트) ---")
    import threading
    
    threads = []
    for i in range(3):
        thread = threading.Thread(
            target=worker_thread, 
            args=(i+1, "production" if i % 2 == 0 else "development")
        )
        threads.append(thread)
        thread.start()
    
    # 모든 스레드 완료 대기
    for thread in threads:
        thread.join()
    
    print("\n--- 환경 전환 테스트 ---")
    provider = SystemFactoryProvider()
    
    # 개발 환경
    provider.set_environment("development")
    dev_factory = provider.get_factory()
    dev_components = dev_factory.create_system_bundle()
    dev_components['logger'].log("Development environment active", "INFO")
    
    # 운영 환경으로 전환
    provider.set_environment("production")
    prod_factory = provider.get_factory()
    prod_components = prod_factory.create_system_bundle()
    prod_components['logger'].log("Production environment active", "INFO")
    
    # 팩토리 인스턴스 비교 (환경별로 다른 싱글톤)
    print(f"\nDifferent environment factories: {id(dev_factory) != id(prod_factory)}")

demonstrate_singleton_relationship()

Builder

Abstract Factory 가 제품군 선택을 담당하고, Builder 가 각 제품의 복잡한 조립 과정을 담당한다.

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
from abc import ABC, abstractmethod
from typing import Optional, Dict, Any

# Product classes
class DatabaseConfiguration:
    def __init__(self):
        self.host: Optional[str] = None
        self.port: Optional[int] = None
        self.database: Optional[str] = None
        self.ssl_enabled: bool = False
        self.connection_pool_size: int = 10
        self.timeout: int = 30
        self.connection_string: Optional[str] = None
    
    def __str__(self):
        return f"Database Config: {self.connection_string}"

class QueryOptimizer:
    def __init__(self):
        self.cache_enabled: bool = False
        self.cache_size: int = 1000
        self.query_timeout: int = 30
        self.parallel_execution: bool = False
        self.optimization_level: str = "basic"
    
    def __str__(self):
        return f"Query Optimizer: {self.optimization_level} level"

# Abstract Builder
class DatabaseBuilder(ABC):
    @abstractmethod
    def build_configuration(self) -> 'DatabaseBuilder':
        pass
    
    @abstractmethod
    def build_optimizer(self) -> 'DatabaseBuilder':
        pass
    
    @abstractmethod
    def get_result(self) -> Dict[str, Any]:
        pass

# Concrete Builders
class MySQLBuilder(DatabaseBuilder):
    def __init__(self):
        self.reset()
    
    def reset(self):
        self.config = DatabaseConfiguration()
        self.optimizer = QueryOptimizer()
    
    def build_configuration(self) -> 'MySQLBuilder':
        """MySQL 특화 설정 구성"""
        self.config.host = "mysql-server"
        self.config.port = 3306
        self.config.ssl_enabled = True
        self.config.connection_pool_size = 20
        self.config.connection_string = f"mysql://{self.config.host}:{self.config.port}"
        print("Building MySQL configuration with optimized settings")
        return self
    
    def build_optimizer(self) -> 'MySQLBuilder':
        """MySQL 특화 최적화 설정"""
        self.optimizer.cache_enabled = True
        self.optimizer.cache_size = 2000
        self.optimizer.optimization_level = "advanced"
        self.optimizer.parallel_execution = True
        print("Building MySQL query optimizer with advanced features")
        return self
    
    def get_result(self) -> Dict[str, Any]:
        result = {
            'configuration': self.config,
            'optimizer': self.optimizer
        }
        self.reset()  # Builder 재사용을 위한 리셋
        return result

class PostgreSQLBuilder(DatabaseBuilder):
    def __init__(self):
        self.reset()
    
    def reset(self):
        self.config = DatabaseConfiguration()
        self.optimizer = QueryOptimizer()
    
    def build_configuration(self) -> 'PostgreSQLBuilder':
        """PostgreSQL 특화 설정 구성"""
        self.config.host = "postgres-server"
        self.config.port = 5432
        self.config.ssl_enabled = True
        self.config.connection_pool_size = 15
        self.config.timeout = 60
        self.config.connection_string = f"postgresql://{self.config.host}:{self.config.port}"
        print("Building PostgreSQL configuration with enterprise settings")
        return self
    
    def build_optimizer(self) -> 'PostgreSQLBuilder':
        """PostgreSQL 특화 최적화 설정"""
        self.optimizer.cache_enabled = True
        self.optimizer.cache_size = 5000
        self.optimizer.optimization_level = "enterprise"
        self.optimizer.parallel_execution = True
        self.optimizer.query_timeout = 120
        print("Building PostgreSQL query optimizer with enterprise features")
        return self
    
    def get_result(self) -> Dict[str, Any]:
        result = {
            'configuration': self.config,
            'optimizer': self.optimizer
        }
        self.reset()
        return result

# Abstract Factory + Builder 조합
class DatabaseSystemFactory(ABC):
    @abstractmethod
    def create_builder(self) -> DatabaseBuilder:
        """Builder 인스턴스 생성 (Factory Method)"""
        pass
    
    def create_complete_database_system(self) -> Dict[str, Any]:
        """Template Method: Builder를 사용한 완전한 시스템 구성"""
        builder = self.create_builder()
        
        # Builder 패턴의 Director 역할 수행
        return (builder
                .build_configuration()
                .build_optimizer()
                .get_result())

class MySQLSystemFactory(DatabaseSystemFactory):
    def create_builder(self) -> DatabaseBuilder:
        return MySQLBuilder()

class PostgreSQLSystemFactory(DatabaseSystemFactory):
    def create_builder(self) -> DatabaseBuilder:
        return PostgreSQLBuilder()

# Director class for complex building scenarios
class DatabaseSystemDirector:
    def __init__(self, factory: DatabaseSystemFactory):
        self.factory = factory
    
    def create_development_system(self) -> Dict[str, Any]:
        """개발 환경용 간단한 구성"""
        builder = self.factory.create_builder()
        return builder.build_configuration().get_result()
    
    def create_production_system(self) -> Dict[str, Any]:
        """운영 환경용 완전한 구성"""
        return self.factory.create_complete_database_system()
    
    def create_high_performance_system(self) -> Dict[str, Any]:
        """고성능 환경용 커스텀 구성"""
        builder = self.factory.create_builder()
        result = (builder
                 .build_configuration()
                 .build_optimizer()
                 .get_result())
        
        # 추가 고성능 설정
        result['configuration'].connection_pool_size *= 2
        result['optimizer'].cache_size *= 3
        print("Applied high-performance enhancements")
        
        return result

# Usage Example
def demonstrate_builder_relationship():
    print("=== Abstract Factory + Builder 조합 관계 ===")
    
    # MySQL 시스템 구성
    mysql_factory = MySQLSystemFactory()
    mysql_director = DatabaseSystemDirector(mysql_factory)
    
    print("--- MySQL 운영 시스템 구성 ---")
    mysql_prod_system = mysql_director.create_production_system()
    print(f"Configuration: {mysql_prod_system['configuration']}")
    print(f"Optimizer: {mysql_prod_system['optimizer']}")
    
    print("\n--- MySQL 고성능 시스템 구성 ---")
    mysql_hp_system = mysql_director.create_high_performance_system()
    print(f"Enhanced Configuration: {mysql_hp_system['configuration']}")
    print(f"Enhanced Optimizer: {mysql_hp_system['optimizer']}")
    
    print("\n" + "-"*50 + "\n")
    
    # PostgreSQL 시스템 구성
    postgres_factory = PostgreSQLSystemFactory()
    postgres_director = DatabaseSystemDirector(postgres_factory)
    
    print("--- PostgreSQL 개발 시스템 구성 ---")
    postgres_dev_system = postgres_director.create_development_system()
    print(f"Dev Configuration: {postgres_dev_system['configuration']}")

demonstrate_builder_relationship()

Factory Method

복잡한 서브시스템의 객체 생성을 단순화하고 싶을 때 Abstract Factory 대신 Facade Pattern 을 사용할 수 있다.

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
from abc import ABC, abstractmethod
from typing import List, Dict, Any, Optional

# Complex Subsystem Components
class DatabaseConnection:
    def __init__(self, host: str, port: int, database: str):
        self.host = host
        self.port = port
        self.database = database
        self.is_connected = False
    
    def connect(self) -> bool:
        print(f"Connecting to database: {self.host}:{self.port}/{self.database}")
        self.is_connected = True
        return True
    
    def disconnect(self) -> None:
        print(f"Disconnecting from database: {self.database}")
        self.is_connected = False

class CacheManager:
    def __init__(self, cache_type: str, max_size: int):
        self.cache_type = cache_type
        self.max_size = max_size
        self.cache_data: Dict[str, Any] = {}
    
    def initialize(self) -> None:
        print(f"Initializing {self.cache_type} cache with max size {self.max_size}")
    
    def set(self, key: str, value: Any) -> None:
        self.cache_data[key] = value
        print(f"Cache set: {key}")
    
    def get(self, key: str) -> Optional[Any]:
        return self.cache_data.get(key)

class MessageQueue:
    def __init__(self, queue_name: str, max_messages: int):
        self.queue_name = queue_name
        self.max_messages = max_messages
        self.messages: List[str] = []
    
    def initialize(self) -> None:
        print(f"Initializing message queue: {queue_name}")
    
    def send_message(self, message: str) -> None:
        if len(self.messages) < self.max_messages:
            self.messages.append(message)
            print(f"Message sent to {self.queue_name}: {message}")

class SecurityManager:
    def __init__(self, encryption_type: str):
        self.encryption_type = encryption_type
        self.is_initialized = False
    
    def initialize(self) -> None:
        print(f"Initializing security with {self.encryption_type} encryption")
        self.is_initialized = True
    
    def encrypt(self, data: str) -> str:
        return f"encrypted({data})"
    
    def decrypt(self, data: str) -> str:
        return data.replace("encrypted(", "").replace(")", "")

# ===== Abstract Factory Approach =====
class SystemComponentFactory(ABC):
    @abstractmethod
    def create_database_connection(self) -> DatabaseConnection:
        pass
    
    @abstractmethod
    def create_cache_manager(self) -> CacheManager:
        pass
    
    @abstractmethod
    def create_message_queue(self) -> MessageQueue:
        pass
    
    @abstractmethod
    def create_security_manager(self) -> SecurityManager:
        pass

class DevelopmentSystemFactory(SystemComponentFactory):
    def create_database_connection(self) -> DatabaseConnection:
        conn = DatabaseConnection("localhost", 5432, "dev_db")
        conn.connect()
        return conn
    
    def create_cache_manager(self) -> CacheManager:
        cache = CacheManager("Redis", 1000)
        cache.initialize()
        return cache
    
    def create_message_queue(self) -> MessageQueue:
        queue = MessageQueue("dev_queue", 100)
        queue.initialize()
        return queue
    
    def create_security_manager(self) -> SecurityManager:
        security = SecurityManager("AES256")
        security.initialize()
        return security

class ProductionSystemFactory(SystemComponentFactory):
    def create_database_connection(self) -> DatabaseConnection:
        conn = DatabaseConnection("prod-db-cluster", 5432, "prod_db")
        conn.connect()
        return conn
    
    def create_cache_manager(self) -> CacheManager:
        cache = CacheManager("Redis Cluster", 10000)
        cache.initialize()
        return cache
    
    def create_message_queue(self) -> MessageQueue:
        queue = MessageQueue("prod_queue", 10000)
        queue.initialize()
        return queue
    
    def create_security_manager(self) -> SecurityManager:
        security = SecurityManager("AES256-GCM")
        security.initialize()
        return security

# Client using Abstract Factory (Complex)
class ComplexSystemClient:
    def __init__(self, factory: SystemComponentFactory):
        self.factory = factory
        self.components = {}
    
    def initialize_system(self):
        """복잡한 시스템 초기화 과정"""
        print("=== Complex System Initialization (Abstract Factory) ===")
        
        # 각 컴포넌트를 개별적으로 생성하고 설정
        self.components['database'] = self.factory.create_database_connection()
        self.components['cache'] = self.factory.create_cache_manager()
        self.components['queue'] = self.factory.create_message_queue()
        self.components['security'] = self.factory.create_security_manager()
        
        # 복잡한 상호 의존성 설정
        self.configure_dependencies()
    
    def configure_dependencies(self):
        """컴포넌트 간 복잡한 의존성 설정"""
        print("Configuring complex dependencies…")
        # 실제로는 매우 복잡한 설정 과정이 필요할 수 있음
        
        cache = self.components['cache']
        cache.set("db_connection_string", str(self.components['database'].host))
        
        queue = self.components['queue']
        queue.send_message("System initialized")

# ===== Facade Approach (Simpler Alternative) =====
class SystemFacade:
    """복잡한 서브시스템을 단순화된 인터페이스로 제공"""
    
    def __init__(self, environment: str = "development"):
        self.environment = environment
        self._database: Optional[DatabaseConnection] = None
        self._cache: Optional[CacheManager] = None
        self._queue: Optional[MessageQueue] = None
        self._security: Optional[SecurityManager] = None
    
    def initialize_system(self) -> None:
        """단순화된 시스템 초기화"""
        print(f"=== Simple System Initialization (Facade) - {self.environment} ===")
        
        # 환경에 따른 적절한 설정으로 모든 컴포넌트를 한번에 초기화
        if self.environment == "development":
            self._initialize_development_system()
        elif self.environment == "production":
            self._initialize_production_system()
        else:
            raise ValueError(f"Unknown environment: {self.environment}")
        
        # 복잡한 의존성 설정을 Facade 내부에서 처리
        self._configure_system()
        
        print("System fully initialized and configured")
    
    def _initialize_development_system(self):
        """개발 환경 시스템 초기화"""
        self._database = DatabaseConnection("localhost", 5432, "dev_db")
        self._database.connect()
        
        self._cache = CacheManager("Redis", 1000)
        self._cache.initialize()
        
        self._queue = MessageQueue("dev_queue", 100)
        self._queue.initialize()
        
        self._security = SecurityManager("AES256")
        self._security.initialize()
    
    def _initialize_production_system(self):
        """운영 환경 시스템 초기화"""
        self._database = DatabaseConnection("prod-db-cluster", 5432, "prod_db")
        self._database.connect()
        
        self._cache = CacheManager("Redis Cluster", 10000)
        self._cache.initialize()
        
        self._queue = MessageQueue("prod_queue", 10000)
        self._queue.initialize()
        
        self._security = SecurityManager("AES256-GCM")
        self._security.initialize()
    
    def _configure_system(self):
        """시스템 설정 - 복잡한 로직을 Facade 내부에서 처리"""
        # 캐시 설정
        self._cache.set("db_host", self._database.host)
        self._cache.set("environment", self.environment)
        
        # 초기 메시지 전송
        self._queue.send_message(f"System initialized in {self.environment} mode")
        
        # 보안 설정 검증
        test_data = "test_security"
        encrypted = self._security.encrypt(test_data)
        decrypted = self._security.decrypt(encrypted)
        assert decrypted == test_data, "Security system validation failed"
    
    # 단순화된 인터페이스 메서드들
    def store_data(self, key: str, value: str) -> None:
        """데이터 저장 - 캐시와 보안을 내부적으로 처리"""
        encrypted_value = self._security.encrypt(value)
        self._cache.set(key, encrypted_value)
        self._queue.send_message(f"Data stored: {key}")
    
    def retrieve_data(self, key: str) -> Optional[str]:
        """데이터 조회 - 복호화까지 내부적으로 처리"""
        encrypted_value = self._cache.get(key)
        if encrypted_value:
            return self._security.decrypt(encrypted_value)
        return None
    
    def send_notification(self, message: str) -> None:
        """알림 전송 - 큐 시스템을 내부적으로 사용"""
        self._queue.send_message(f"NOTIFICATION: {message}")
    
    def get_system_status(self) -> Dict[str, str]:
        """시스템 상태 조회 - 모든 컴포넌트 상태를 한번에 확인"""
        return {
            "database": "connected" if self._database.is_connected else "disconnected",
            "cache": f"{self._cache.cache_type} initialized",
            "queue": f"{len(self._queue.messages)} messages in queue",
            "security": f"{self._security.encryption_type} encryption active",
            "environment": self.environment
        }
    
    def shutdown_system(self) -> None:
        """시스템 종료 - 모든 컴포넌트를 안전하게 종료"""
        print("Shutting down system…")
        if self._database:
            self._database.disconnect()
        
        if self._queue:
            self._queue.send_message("System shutting down")
        
        print("System shutdown complete")

# Client using Facade (Simple)
class SimpleSystemClient:
    def __init__(self, environment: str = "development"):
        self.facade = SystemFacade(environment)
    
    def run_application(self):
        """단순화된 애플리케이션 실행"""
        print("=== Running Application with Facade ===")
        
        # 한 줄로 전체 시스템 초기화
        self.facade.initialize_system()
        
        # 단순한 비즈니스 로직
        self.facade.store_data("user_1", "John Doe")
        self.facade.store_data("user_2", "Jane Smith")
        
        user_data = self.facade.retrieve_data("user_1")
        print(f"Retrieved user data: {user_data}")
        
        self.facade.send_notification("New user registered")
        
        # 시스템 상태 확인
        status = self.facade.get_system_status()
        print(f"System Status: {status}")
        
        # 정리
        self.facade.shutdown_system()

# Comparison Demo
def demonstrate_facade_vs_abstract_factory():
    print("=== Facade vs Abstract Factory 비교 ===\n")
    
    print("1. Abstract Factory 방식 (복잡하지만 유연함)")
    print("-" * 50)
    
    # Abstract Factory 사용
    dev_factory = DevelopmentSystemFactory()
    complex_client = ComplexSystemClient(dev_factory)
    complex_client.initialize_system()
    
    # 클라이언트가 모든 컴포넌트를 직접 다뤄야 함
    print("클라이언트가 직접 컴포넌트 조작:")
    cache = complex_client.components['cache']
    cache.set("manual_key", "manual_value")
    
    queue = complex_client.components['queue']
    queue.send_message("Manual message from client")
    
    print("\n" + "="*60 + "\n")
    
    print("2. Facade 방식 (간단하지만 덜 유연함)")
    print("-" * 50)
    
    # Facade 사용
    simple_client = SimpleSystemClient("development")
    simple_client.run_application()
    
    print("\n" + "="*60 + "\n")
    
    print("3. 비교 결과")
    print("-" * 50)
    print("Abstract Factory:")
    print("  + 높은 유연성 - 각 컴포넌트를 독립적으로 제어 가능")
    print("  + 확장성 - 새로운 제품군 추가 용이")
    print("  - 복잡성 - 클라이언트가 많은 것을 알아야 함")
    print("  - 초기 설정 복잡 - 의존성 관리가 클라이언트 책임")
    
    print("\nFacade:")
    print("  + 단순성 - 클라이언트가 간단한 인터페이스만 사용")
    print("  + 사용 편의성 - 복잡한 로직이 Facade 내부에 숨겨짐")
    print("  - 유연성 제한 - 미리 정의된 시나리오만 지원")
    print("  - 확장성 제한 - 새로운 요구사항 시 Facade 수정 필요")
    
    print("\n추천 사용 케이스:")
    print("Abstract Factory: 다양한 플랫폼/환경 지원, 고도의 커스터마이징 필요")
    print("Facade: 복잡한 서브시스템의 단순화된 사용, 일반적인 사용 패턴")

demonstrate_facade_vs_abstract_factory()

장점

항목설명관련 원인 또는 기반 원칙
제품군 일관성 보장동일 팩토리에서 생성된 객체들이 스타일·기능적으로 호환되어 UI/UX 및 동작 일관성 유지추상 팩토리 인터페이스가 동일 제품군 생성만 허용
구체 클래스 은닉클라이언트가 구체 클래스 이름이나 생성 방식에 대해 알 필요 없이 사용 가능추상 인터페이스 기반 접근
결합도 감소클라이언트가 인터페이스에만 의존하므로 구체 구현과의 결합이 줄어듬DIP(의존성 역전 원칙), SRP(단일 책임 원칙)
확장성 향상새로운 제품군을 추가할 때 기존 클라이언트 코드 수정 없이 확장 가능OCP(개방 폐쇄 원칙) 준수, 팩토리 교체로 확장 가능
플랫폼 독립성 확보동일 클라이언트 코드로 Windows, Mac, Linux 등 다양한 플랫폼 대상의 객체 생성 가능추상화된 팩토리 구성
유지보수성 향상객체 생성 로직이 팩토리에 집중되므로 수정이 용이하고 영향 범위가 제한됨생성 책임의 분리 (SRP), 팩토리 캡슐화 구조
테스트 용이성 증가DI(의존성 주입) 를 통해 Mock Factory 또는 Stub 으로 테스트 가능객체 생성 제어권이 클라이언트 외부에 있음
제품군 교체 용이성팩토리 인스턴스만 변경하면 전체 제품군 교체가 가능하여 UI/엔진/테마 전환에 용이런타임 팩토리 교체 구조
타입 안전성 제공컴파일 타임에 제품군 간 타입 불일치 방지 가능팩토리 메서드 시그니처로 타입 고정
모듈화 및 재사용성 증가제품 생성 책임을 별도 팩토리로 분리함으로써 클라이언트 모듈의 독립성과 재사용성 향상객체 생성 코드의 집중화와 구조적 분리

단점과 문제점 그리고 해결방안

단점

항목설명해결 방안
클래스 수 증가팩토리, 제품 등 관련 클래스와 인터페이스 수가 많아져 복잡도 증가코드 제너레이션 도구 활용, 모듈 구조 및 네이밍 일관성 유지
구조 복잡성추상 계층과 관계 구조가 복잡하여 초급 개발자 이해 어려움도식화된 문서, 예제 중심 교육, 디자인 가이드 제공
과도한 추상화간단한 문제에 적용 시 오히려 설계 과잉이 되어 유지보수 어려움요구 사항 및 시스템 규모에 따라 Factory Method 로 대체 검토
확장 어려움제품군 추가 시 인터페이스 및 모든 팩토리 구현체를 수정해야 하는 부담디폴트 구현 또는 Optional 인터페이스 메서드 제공, 유연한 인터페이스 설계
메모리 오버헤드불필요한 객체 생성으로 인한 리소스 낭비 가능객체 풀링 (Pooling), Flyweight Pattern 적용

문제점

항목원인영향탐지 및 진단예방 방법해결 방법 및 기법
팩토리 선택 오류설정값 오류, 조건문 잘못된 분기의도하지 않은 제품군 생성유닛 테스트, 로깅조건 로직 검증, 타입 기반 분기 명확화팩토리 검증 로직 추가, 레지스트리 기반 팩토리 매핑 적용
제품 불일치서로 다른 제품군의 객체를 혼용런타임 호환성 오류정적 타입 체크, 컴파일 오류 탐지동일 제품군 전용 팩토리만 사용, 생성 책임 명확화제품군 태그 기반 검증 메커니즘, 타입 안전한 팩토리 구성
인터페이스 변경 부담추상 팩토리에 메서드 추가 필요 시 전체 구현체 영향구현체 재작성 필요코드 리뷰 및 인터페이스 변경 추적 도구확장 고려한 분리 인터페이스 설계, 인터페이스 안정성 확보인터페이스 버전 관리, 파라미터화된 팩토리 사용
순환 의존성팩토리 간 상호 참조 및 의존 관계 형성객체 생성 실패, 사이클 오류의존성 분석 도구, DI 컨테이너 확인의존 방향 단방향 유지, 책임 분리 설계계층화된 팩토리 구조 설계, DI/IoC 컨테이너 활용
팩토리 난립/폭주제품군 수 증가로 다양한 팩토리 생김관리 어려움, 유지보수 비용 증가구조 시각화, 구성도 분석팩토리 그룹화 및 메타팩토리 도입추상화 계층 통합, 팩토리 컴포지션 또는 Provider 패턴 적용
메모리 누수팩토리 또는 제품 객체의 라이프사이클 미관리성능 저하 및 리소스 부족메모리 프로파일링, GC 로그 분석객체 생명주기 명시적 관리자원 해제 로직 추가, GC 친화적 설계, WeakRef 활용 등

도전 과제

도전 과제원인영향예방 방법해결 방법 및 기법
제품군 확장 시 모든 팩토리 수정 필요추상 팩토리 인터페이스가 고정되어 새로운 제품 추가 시 전면 수정 필요구현 클래스의 확장 부담, 코드 변경 범위 확장유연한 인터페이스 분리 설계, Optional 메서드 활용파라미터화된 팩토리, default method, Fallback 구조 적용
클래스 수 증가로 인한 구조 복잡도제품군 + 팩토리 수 증가, 세분화된 인터페이스 구성코드 가독성 저하, 유지보수성 저하문서화 및 패키지 구조 명확화, 네이밍 컨벤션 통일코드 생성 도구 활용, DI 컨테이너 조합, 구조 시각화 도구 도입
런타임 팩토리 선택의 복잡도 증가동적 환경에서 조건문 또는 설정 기반으로 팩토리 선택 필요팩토리 선택 오류 발생 가능성, 테스트 난이도 증가설정 기반 매핑, 타입 기반 팩토리 선택 기준 마련FactoryProvider 또는 레지스트리 기반 팩토리 매핑 전략
마이크로서비스/클라우드 환경과의 결합 어려움서비스 분산 환경, 컨테이너 기반 아키텍처의 동적 스케일링전역 팩토리 관리 어려움, 인스턴스 상태 불일치 발생 가능Stateless 설계, DI 기반 팩토리 구성서비스 레지스트리 및 클라우드 서비스와 연동된 분산 팩토리 구현
실시간 시스템에서의 객체 생성 오버헤드객체 생성 비용 및 초기화 지연성능 저하, 응답 시간 증가최소 생성 전략 도입, 미리 준비된 객체 활용객체 풀링 (Pooling), 지연 초기화 (Lazy Initialization) 적용
테스트 환경에서의 Mock 객체 주입 어려움팩토리 내부에 생성 로직이 고정되어 테스트 주입 어려움테스트 격리 어려움, 테스트 신뢰도 저하인터페이스 기반 팩토리 추상화, 테스트 전용 팩토리 설계DI 컨테이너 활용, Mock Factory 또는 Factory Override 전략 적용
인터페이스 변경 시 전체 구현체 영향단일 인터페이스에 다수의 제품 생성 메서드 포함모든 팩토리 구현체에 영향 발생인터페이스 분리 원칙 (ISP) 적용, 단일 책임 유지인터페이스 세분화 및 버전 관리, 플러그인 기반 구조 도입
순환 의존성 문제서로 다른 팩토리가 내부적으로 참조 관계 형성객체 생성 시 오류, 의존성 꼬임DI 원칙 적용, 참조 방향 일관화계층화된 팩토리 구성, Service Locator 또는 Factory Chain 패턴 적용
오버엔지니어링 위험간단한 구조에도 무리하게 추상 팩토리 적용불필요한 복잡성, 생산성 저하요구사항 기반 사전 설계 검토Factory Method → 필요 시 Abstract Factory 로 확장 전략 사용
유지보수 및 확장성 저하과도한 수작업 클래스 추가, 인터페이스 의존확장 어려움, 유지보수 비용 증가제품군 증가 예측에 기반한 팩토리 추상화 수준 조절코드 템플릿 자동화, 유닛 테스트 기반 리팩토링 루틴 확보

분류 기준에 따른 종류 및 유형

분류 기준유형설명특징
구현 방식클래스 기반클래스 상속을 통한 구현정적 타입 안정성, 컴파일 타임 검증
구현 방식인터페이스 기반인터페이스 구현을 통한 구현유연성, 다중 상속 효과
팩토리 생성정적 팩토리컴파일 타임에 팩토리 결정성능 우수, 타입 안정성
팩토리 생성동적 팩토리런타임에 팩토리 결정유연성, 확장성
제품 생성즉시 생성요청 즉시 객체 생성단순함, 메모리 사용량 예측 가능
제품 생성지연 생성실제 사용 시점에 객체 생성메모리 효율성, 초기화 시간 단축

실무 사용 예시

분야사용 사례조합 기술/패턴목적 및 효과
1. UI/GUI 개발운영체제별 위젯/컴포넌트 생성Factory Method, Builder, MVC플랫폼별 UI 일관성, 크로스 플랫폼 호환성
Java Swing / Look & FeelAbstract Factory테마 기반 UI 구성 지원
웹 UI 프레임워크 구성DI Container, AOP다중 환경 대응 UI 로직 구성
게임 UI 테마 적용Unity/Unreal + Theme Factory게임 내 UI 아트 스타일 일관성
2. 데이터베이스RDBMS 커넥션 객체 팩토리JDBC, DAO, ORMDB 벤더 독립성, DB 변경 유연성
NoSQL/RDB 동시 대응 드라이버Parameterized Factory복수 DB 대응 및 런타임 DB 선택
ADO.NET Provider 구성.NET 기반 Factory + AdapterDB 액세스 계층 캡슐화
3. 클라우드/백엔드클라우드 벤더별 클라이언트 구성Adapter, Strategy, BridgeAWS/Azure 등 SDK 독립성 확보
API 버전별 핸들러 추상화Abstract Factory + Version MapperAPI 버전 전환 시 코드 변경 최소화
로깅 시스템 백엔드 구성Strategy Pattern + Factory콘솔/파일/원격 로거 교체 유연성
4. 게임 개발레벨/테마 기반 적 캐릭터 생성Factory + Strategy, State Pattern게임 밸런싱, 다양성 보장
아이템/무기 스타일팩 생성Theme-aware Factory테마별 일관된 게임 요소 생성
5. 웹 애플리케이션템플릿 엔진/렌더링 컴포넌트 구성Template Engine + Factory다양한 템플릿 엔진과 통합 가능
멀티 테넌시 서비스 객체 주입DI, Plugin 기반 FactoryProvider테넌트별 환경 맞춤 서비스 제공
6. IoT/임베디드디바이스 타입별 드라이버 팩토리Protocol Factory (MQTT/CoAP)하드웨어 독립성, 통신 최적화
제조사별 센서 인터페이스 관리Adapter + Factory센서 교체 시 호환성 보장
7. 테스트 환경Mock 객체 vs 실제 객체 팩토리DI 기반 Test Factory, Stub Factory테스트 유연성 향상, 테스트 코드 분리
테스트 환경 구성용 팩토리 교체환경 프로파일 기반 구성테스트/운영 환경 간 분리 및 검증 용이성

활용 사례

사례 1: 전자상거래 결제 시스템

대형 전자상거래 플랫폼에서 국가별로 다른 결제 시스템을 지원해야 하는 상황. 한국은 KakaoPay 와 Toss, 미국은 PayPal 과 Stripe, 일본은 Rakuten Pay 와 LINE Pay 를 지원해야 함.

시스템 구성

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
PaymentFactory (추상 팩토리)
├── KoreaPaymentFactory (한국 결제 팩토리)
│   ├── KakaoPayProcessor
│   └── TossPayProcessor
├── USPaymentFactory (미국 결제 팩토리)
│   ├── PayPalProcessor
│   └── StripeProcessor
└── JapanPaymentFactory (일본 결제 팩토리)
    ├── RakutenPayProcessor
    └── LinePayProcessor

시스템 다이어그램

graph TB
    A[E-Commerce Platform] --> B[PaymentFactoryProvider]
    B --> C[KoreaPaymentFactory]
    B --> D[USPaymentFactory]
    B --> E[JapanPaymentFactory]
    
    C --> F[KakaoPayProcessor]
    C --> G[TossPayProcessor]
    D --> H[PayPalProcessor]
    D --> I[StripeProcessor]
    E --> J[RakutenPayProcessor]
    E --> K[LinePayProcessor]
    
    F --> L[PaymentProcessor Interface]
    G --> L
    H --> L
    I --> L
    J --> L
    K --> L

Workflow:

  1. 사용자 접속: 사용자의 국가 정보 확인 (IP 기반 또는 계정 설정)
  2. 팩토리 선택: 국가별 결제 팩토리 선택 (KoreaPaymentFactory)
  3. 결제 수단 선택: 사용자가 원하는 결제 수단 선택 (KakaoPay)
  4. 프로세서 생성: 팩토리에서 해당 결제 프로세서 객체 생성
  5. 결제 처리: 통일된 인터페이스를 통한 결제 진행
  6. 결과 처리: 성공/실패 결과를 표준화된 형태로 반환

사례 2: GUI 테마 시스템 구현 사례

시스템 구성

graph TB
    Client[애플리케이션 클라이언트]
    ThemeFactory[테마 팩토리 인터페이스]
    LightFactory[라이트 테마 팩토리]
    DarkFactory[다크 테마 팩토리]
    ButtonInterface[버튼 인터페이스]
    PanelInterface[패널 인터페이스]
    LightButton[라이트 버튼]
    LightPanel[라이트 패널]
    DarkButton[다크 버튼]
    DarkPanel[다크 패널]
    
    Client --> ThemeFactory
    ThemeFactory --> LightFactory
    ThemeFactory --> DarkFactory
    LightFactory --> LightButton
    LightFactory --> LightPanel
    DarkFactory --> DarkButton
    DarkFactory --> DarkPanel
    ButtonInterface --> LightButton
    ButtonInterface --> DarkButton
    PanelInterface --> LightPanel
    PanelInterface --> DarkPanel

Workflow

  1. 사용자 테마 선택: 사용자가 설정에서 라이트/다크 테마 선택
  2. 팩토리 결정: 설정값에 따라 적절한 테마 팩토리 선택
  3. UI 컴포넌트 생성: 선택된 팩토리를 통해 일관된 테마의 컴포넌트 생성
  4. 화면 렌더링: 생성된 컴포넌트들로 일관된 Look & Feel 의 UI 구성

Abstract Factory Pattern 의 역할

패턴 유무에 따른 차이점

사례 3: 크로스 플랫폼 GUI 애플리케이션

온라인 상거래 플랫폼에서 Windows, macOS, Linux 를 지원하는 데스크톱 애플리케이션을 개발하는 경우

시스템 구성

graph TB
    subgraph "Client Application"
        A[Order Management UI]
        B[Product Catalog UI]
        C[Payment Processing UI]
    end
    
    subgraph "Abstract Factory Layer"
        D[UI Component Factory]
    end
    
    subgraph "Platform Factories"
        E[Windows Factory]
        F[macOS Factory]
        G[Linux Factory]
    end
    
    subgraph "Platform Components"
        H[Windows Button]
        I[Windows Dialog]
        J[macOS Button]
        K[macOS Dialog]
        L[Linux Button]
        M[Linux Dialog]
    end
    
    A --> D
    B --> D
    C --> D
    D --> E
    D --> F
    D --> G
    E --> H
    E --> I
    F --> J
    F --> K
    G --> L
    G --> M

Workflow

  1. 초기화 단계: 플랫폼 감지 후 적절한 팩토리 선택
  2. UI 구성 단계: Abstract Factory 를 통해 플랫폼별 컴포넌트 생성
  3. 이벤트 처리 단계: 공통 인터페이스를 통한 이벤트 처리
  4. 렌더링 단계: 플랫폼별 최적화된 렌더링 수행

Abstract Factory Pattern 의 역할

패턴 유무에 따른 차이점

구현 예시

Python

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
from abc import ABC, abstractmethod
from typing import Protocol

# Abstract Products
class Button(Protocol):
    def render(self) -> str:
        pass
    
    def on_click(self, handler) -> None:
        pass

class Dialog(Protocol):
    def show(self) -> str:
        pass
    
    def close(self) -> None:
        pass

# Abstract Factory
class UIComponentFactory(ABC):
    @abstractmethod
    def create_button(self) -> Button:
        pass
    
    @abstractmethod
    def create_dialog(self) -> Dialog:
        pass

# Concrete Products - Windows
class WindowsButton:
    def __init__(self):
        self._handlers = []
    
    def render(self) -> str:
        return "Rendering Windows-style button with native controls"
    
    def on_click(self, handler) -> None:
        self._handlers.append(handler)
        print("Windows button click event registered")

class WindowsDialog:
    def __init__(self):
        self._visible = False
    
    def show(self) -> str:
        self._visible = True
        return "Displaying Windows modal dialog with system theme"
    
    def close(self) -> None:
        self._visible = False
        print("Windows dialog closed")

# Concrete Products - macOS
class MacOSButton:
    def __init__(self):
        self._handlers = []
    
    def render(self) -> str:
        return "Rendering macOS-style button with Cocoa controls"
    
    def on_click(self, handler) -> None:
        self._handlers.append(handler)
        print("macOS button click event registered")

class MacOSDialog:
    def __init__(self):
        self._visible = False
    
    def show(self) -> str:
        self._visible = True
        return "Displaying macOS sheet dialog with native appearance"
    
    def close(self) -> None:
        self._visible = False
        print("macOS dialog closed")

# Concrete Factories
class WindowsUIFactory(UIComponentFactory):
    def create_button(self) -> Button:
        return WindowsButton()
    
    def create_dialog(self) -> Dialog:
        return WindowsDialog()

class MacOSUIFactory(UIComponentFactory):
    def create_button(self) -> Button:
        return MacOSButton()
    
    def create_dialog(self) -> Dialog:
        return MacOSDialog()

# Factory Provider (선택적 구성요소)
class UIFactoryProvider:
    @staticmethod
    def get_factory(platform: str) -> UIComponentFactory:
        factories = {
            "windows": WindowsUIFactory(),
            "macos": MacOSUIFactory()
        }
        
        factory = factories.get(platform.lower())
        if not factory:
            raise ValueError(f"Unsupported platform: {platform}")
        
        return factory

# Client Code
class OrderManagementApp:
    def __init__(self, ui_factory: UIComponentFactory):
        self._factory = ui_factory
        self._submit_button = None
        self._confirmation_dialog = None
    
    def initialize_ui(self):
        # Create UI components using the factory
        self._submit_button = self._factory.create_button()
        self._confirmation_dialog = self._factory.create_dialog()
        
        # Configure components
        self._submit_button.on_click(self._handle_submit)
    
    def _handle_submit(self):
        print("Order submission initiated")
        dialog_message = self._confirmation_dialog.show()
        print(dialog_message)
    
    def run(self):
        print("Initializing Order Management Application")
        self.initialize_ui()
        
        # Simulate user interaction
        button_render = self._submit_button.render()
        print(button_render)
        
        # Simulate button click
        self._handle_submit()

# Usage Example
def main():
    import platform
    
    # Detect platform and create appropriate factory
    current_platform = platform.system().lower()
    platform_map = {
        "windows": "windows",
        "darwin": "macos"  # macOS system name
    }
    
    mapped_platform = platform_map.get(current_platform, "windows")
    
    try:
        factory = UIFactoryProvider.get_factory(mapped_platform)
        app = OrderManagementApp(factory)
        app.run()
        
        print(f"\nApplication running on {mapped_platform} with native UI components")
        
    except ValueError as e:
        print(f"Error: {e}")

if __name__ == "__main__":
    main()

Javascript

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
// Abstract Factory Pattern in JavaScript with ES6+ features

// Abstract Products using Symbol for private methods
const _render = Symbol('render');
const _onClick = Symbol('onClick');

class AbstractButton {
    constructor() {
        if (new.target === AbstractButton) {
            throw new TypeError("Cannot construct AbstractButton instances directly");
        }
    }
    
    [_render]() {
        throw new Error("render method must be implemented");
    }
    
    [_onClick](handler) {
        throw new Error("onClick method must be implemented");
    }
    
    // Public interface
    render() { return this[_render](); }
    onClick(handler) { return this[_onClick](handler); }
}

class AbstractDialog {
    constructor() {
        if (new.target === AbstractDialog) {
            throw new TypeError("Cannot construct AbstractDialog instances directly");
        }
    }
    
    show() {
        throw new Error("show method must be implemented");
    }
    
    close() {
        throw new Error("close method must be implemented");
    }
}

// Concrete Products - Windows Implementation
class WindowsButton extends AbstractButton {
    constructor() {
        super();
        this.handlers = [];
        this.isPressed = false;
    }
    
    [_render]() {
        return {
            type: 'windows-button',
            style: 'native-windows-theme',
            state: this.isPressed ? 'pressed' : 'normal'
        };
    }
    
    [_onClick](handler) {
        this.handlers.push(handler);
        console.log('Windows button click handler registered');
    }
    
    // Windows-specific methods
    setWindowsTheme(theme) {
        this.theme = theme;
        return this;
    }
}

class WindowsDialog extends AbstractDialog {
    constructor() {
        super();
        this.isModal = true;
        this.visible = false;
    }
    
    show() {
        this.visible = true;
        return {
            type: 'windows-dialog',
            modal: this.isModal,
            theme: 'system-theme'
        };
    }
    
    close() {
        this.visible = false;
        console.log('Windows dialog closed with animation');
    }
    
    setModal(isModal) {
        this.isModal = isModal;
        return this;
    }
}

// Concrete Products - macOS Implementation
class MacOSButton extends AbstractButton {
    constructor() {
        super();
        this.handlers = [];
        this.cornerRadius = 8;
    }
    
    [_render]() {
        return {
            type: 'macos-button',
            style: 'cocoa-theme',
            cornerRadius: this.cornerRadius,
            animation: 'fade-in'
        };
    }
    
    [_onClick](handler) {
        this.handlers.push(handler);
        console.log('macOS button click handler registered with Core Animation');
    }
    
    // macOS-specific methods
    setCornerRadius(radius) {
        this.cornerRadius = radius;
        return this;
    }
}

class MacOSDialog extends AbstractDialog {
    constructor() {
        super();
        this.style = 'sheet';
        this.visible = false;
    }
    
    show() {
        this.visible = true;
        return {
            type: 'macos-dialog',
            style: this.style,
            animation: 'slide-down',
            backdrop: 'blur'
        };
    }
    
    close() {
        this.visible = false;
        console.log('macOS dialog closed with slide-up animation');
    }
    
    setStyle(style) {
        this.style = style;
        return this;
    }
}

// Abstract Factory
class UIComponentFactory {
    createButton() {
        throw new Error("createButton method must be implemented");
    }
    
    createDialog() {
        throw new Error("createDialog method must be implemented");
    }
    
    // Template method for creating a complete UI set
    createUISet() {
        return {
            button: this.createButton(),
            dialog: this.createDialog()
        };
    }
}

// Concrete Factories
class WindowsUIFactory extends UIComponentFactory {
    createButton() {
        return new WindowsButton();
    }
    
    createDialog() {
        return new WindowsDialog();
    }
}

class MacOSUIFactory extends UIComponentFactory {
    createButton() {
        return new MacOSButton();
    }
    
    createDialog() {
        return new MacOSDialog();
    }
}

// Factory Provider with Singleton pattern
class UIFactoryProvider {
    static instance = null;
    static factories = new Map();
    
    static getInstance() {
        if (!UIFactoryProvider.instance) {
            UIFactoryProvider.instance = new UIFactoryProvider();
        }
        return UIFactoryProvider.instance;
    }
    
    static registerFactory(platform, factory) {
        UIFactoryProvider.factories.set(platform, factory);
    }
    
    static getFactory(platform) {
        const factory = UIFactoryProvider.factories.get(platform);
        if (!factory) {
            throw new Error(`No factory registered for platform: ${platform}`);
        }
        return factory;
    }
    
    static detectPlatform() {
        const userAgent = navigator.userAgent.toLowerCase();
        if (userAgent.includes('win')) return 'windows';
        if (userAgent.includes('mac')) return 'macos';
        return 'windows'; // default fallback
    }
}

// Registration
UIFactoryProvider.registerFactory('windows', new WindowsUIFactory());
UIFactoryProvider.registerFactory('macos', new MacOSUIFactory());

// Client Application
class ECommerceApp {
    constructor() {
        const platform = UIFactoryProvider.detectPlatform();
        this.factory = UIFactoryProvider.getFactory(platform);
        this.components = null;
    }
    
    initialize() {
        console.log('Initializing E-Commerce Application…');
        this.components = this.factory.createUISet();
        
        // Configure components
        this.setupEventHandlers();
        
        return this;
    }
    
    setupEventHandlers() {
        this.components.button.onClick(() => {
            console.log('Purchase button clicked');
            this.showConfirmation();
        });
    }
    
    showConfirmation() {
        const dialogInfo = this.components.dialog.show();
        console.log('Confirmation dialog displayed:', dialogInfo);
    }
    
    render() {
        const buttonInfo = this.components.button.render();
        console.log('Button rendered:', buttonInfo);
        
        return {
            button: buttonInfo,
            dialog: this.components.dialog.visible
        };
    }
}

// Usage and Testing
function runExample() {
    try {
        const app = new ECommerceApp();
        app.initialize();
        
        const renderInfo = app.render();
        console.log('Application rendered successfully:', renderInfo);
        
        // Simulate user interaction
        app.showConfirmation();
        
    } catch (error) {
        console.error('Application error:', error.message);
    }
}

// Execute example
runExample();

Java

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
// 추상 팩토리
public interface GUIFactory {
    Button createButton();
    Checkbox createCheckbox();
}

// 구체적 팩토리 - Windows
public class WindowsFactory implements GUIFactory {
    public Button createButton() {
        return new WindowsButton();
    }
    
    public Checkbox createCheckbox() {
        return new WindowsCheckbox();
    }
}

// 구체적 팩토리 - Mac
public class MacFactory implements GUIFactory {
    public Button createButton() {
        return new MacButton();
    }
    
    public Checkbox createCheckbox() {
        return new MacCheckbox();
    }
}

// 추상 제품
public interface Button {
    void paint();
}

public interface Checkbox {
    void paint();
}

// 구체적 제품
public class WindowsButton implements Button {
    public void paint() {
        System.out.println("Windows 스타일 버튼 렌더링");
    }
}

public class MacButton implements Button {
    public void paint() {
        System.out.println("Mac 스타일 버튼 렌더링");
    }
}

// 클라이언트 코드
public class Application {
    private Button button;
    private Checkbox checkbox;
    
    public Application(GUIFactory factory) {
        button = factory.createButton();
        checkbox = factory.createCheckbox();
    }
    
    public void paint() {
        button.paint();
        checkbox.paint();
    }
}

// 사용 예시
public class Demo {
    public static void main(String[] args) {
        GUIFactory factory;
        String osName = System.getProperty("os.name").toLowerCase();
        
        if (osName.contains("mac")) {
            factory = new MacFactory();
        } else {
            factory = new WindowsFactory();
        }
        
        Application app = new Application(factory);
        app.paint();
    }
}

실무에서 효과적으로 적용하기 위한 고려사항 및 주의할 점

구분항목설명권장사항
설계 단계제품군 정의관련 객체들을 논리적으로 그룹화하고, 제품군 간 역할과 책임을 명확히 정의도메인 전문가와 협업하여 실제 사용 케이스 기반 설계
인터페이스 안정성추상 인터페이스는 자주 변경되지 않도록 최소 기능만 정의확장 가능한 구조와 신중한 초기 설계
복잡도 관리과도한 추상화와 불필요한 클래스 생성은 오히려 유지보수 어려움으로 이어짐필요 시에만 적용, 단순한 문제에는 단순한 해결책
확장 계획새로운 제품군 추가를 고려한 구조를 갖추지 않으면 변경이 전체에 영향을 미침확장 포인트를 사전에 정의하거나 기본 구현 제공
플랫폼/환경 다양성다양한 OS, 클라우드, 테스트 환경 등에서의 적용 가능성DI 컨테이너 및 설정 기반 팩토리 선택 구조 활용
구현 단계인터페이스 설계클래스 간 의존을 줄이고, 클라이언트는 추상 인터페이스만 알게 설계구체 구현을 숨기고 인터페이스 중심 설계
팩토리 등록/선택 메커니즘런타임 환경에 따라 다른 팩토리를 선택해야 하는 경우가 많음설정 파일, 전략 패턴, FactoryProvider 활용
예외 처리잘못된 팩토리 인스턴스나 제품 생성 실패 시 적절한 핸들링 필요검증 로직 추가 및 기본/디폴트 처리 제공
클래스 수 증가제품군/팩토리 수만큼 클래스 수 증가 → 유지보수 부담 가능코드 생성기, 공통 로직 추출, 패키지 구조 체계화
운영 단계성능 최적화무거운 객체나 빈번히 생성되는 객체의 경우 성능에 악영향지연 초기화 (Lazy Initialization), 객체 풀링 적용
로깅 및 추적어떤 팩토리/제품이 사용되었는지 운영 환경에서 확인이 어려울 수 있음팩토리 사용 로그, 제품 생성 이벤트 추적 로깅 구현
모니터링팩토리 기반 시스템에서 성능 병목이나 메모리 누수가 발생할 수 있음메모리/GC 프로파일링 도구, 팩토리별 사용량 수집
테스트 단계테스트 전략제품 객체가 많고 상태가 다양한 경우 테스트 코드가 복잡해질 수 있음인터페이스 기반 설계 + Mock 팩토리 분리 구현
DI 활용테스트 환경에서 팩토리를 쉽게 바꿀 수 있어야 함DI 컨테이너/환경 프로파일 기반 객체 주입
제품군별 격리 테스트제품군 간 의존이나 혼용 시 버그 가능성 존재제품군별 독립적인 테스트와 타입 검증 체계 설계

관련 기술과 연계

기술 영역연계 방식설명
Dependency Injection (DI)Factory 주입Spring, Guice 등에서 팩토리를 DI 로 관리 가능
Configuration Management설정 기반 팩토리 선택설정값에 따라 팩토리 동적 선택 (e.g. properties, YAML)
Plugin Architecture팩토리 등록 방식외부 모듈의 제품군을 플러그인 팩토리로 등록/로딩
GUI Toolkits테마별 제품군 제공각 OS 별 버튼, 메뉴 등 GUI 컴포넌트를 팩토리로 구성

Abstract Factory 패턴 활용 시 흔한 실수

실수설명개선 방안
팩토리 기능 과도 집중Factory 클래스에 논리 추가로 SRP 위반팩토리는 오직 객체 생성만 담당
팩토리 간 결합 발생팩토리 내부에서 다른 팩토리 직접 생성DI 또는 서비스 로케이터 패턴 적용
제품군 확장 시 인터페이스 확장 반복인터페이스 변경이 연쇄 영향을 줌인터페이스의 최소화를 유지, 여러 인터페이스로 분리 고려
팩토리 기능 과도 집중 (SRP 위반)
잘못된 예시
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
class UIFactory:
    def create_button(self):
        return WindowsButton()

    def create_window(self):
        return WindowsWindow()

    # ❌ 잘못: 팩토리 내부에 UI 논리까지 포함 → SRP 위반
    def render_ui(self):
        button = self.create_button()
        window = self.create_window()
        print("Rendering UI...")
        button.draw()
        window.open()

문제점: 팩토리가 객체 생성뿐 아니라 렌더링 로직까지 담당하며, 단일 책임 원칙 (SRP) 을 위반하고 있음.

개선된 예시
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
# 순수한 팩토리: 생성 책임만 가짐
class UIFactory:
    def create_button(self):
        return WindowsButton()

    def create_window(self):
        return WindowsWindow()

# 클라이언트가 비즈니스 로직과 UI 사용을 담당
class UIClient:
    def __init__(self, factory: UIFactory):
        self.factory = factory

    def render_ui(self):
        button = self.factory.create_button()
        window = self.factory.create_window()
        print("Rendering UI...")
        button.draw()
        window.open()
팩토리 간 결합 발생
잘못된 예시
1
2
3
4
5
6
7
8
class UIFactory:
    def __init__(self):
        # ❌ 잘못: 팩토리 내부에서 직접 다른 팩토리 생성 → 결합도 상승
        self.theme_factory = ThemeFactory()

    def create_button(self):
        theme = self.theme_factory.get_theme()
        return ThemedButton(theme)

📛 문제점: 팩토리 안에서 또 다른 팩토리를 직접 생성하고 있어, 결합도 증가 및 테스트 어려움 초래

개선된 예시 (DI 적용)
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
class UIFactory:
    def __init__(self, theme_factory):
        self.theme_factory = theme_factory  # DI (의존성 주입)

    def create_button(self):
        theme = self.theme_factory.get_theme()
        return ThemedButton(theme)

# 주입 시점에서 팩토리를 구성
ui_factory = UIFactory(theme_factory=ThemeFactory())

또는 서비스 로케이터 패턴 사용 예시:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
class ServiceLocator:
    _services = {}

    @classmethod
    def register(cls, name, service):
        cls._services[name] = service

    @classmethod
    def resolve(cls, name):
        return cls._services[name]

# 등록
ServiceLocator.register("theme_factory", ThemeFactory())

# 사용
theme_factory = ServiceLocator.resolve("theme_factory")
ui_factory = UIFactory(theme_factory)
제품군 확장 시 인터페이스 확장 반복
잘못된 예시
1
2
3
4
5
6
7
8
9
# 초기에는 버튼만 있었음
class AbstractFactory:
    def create_button(self):
        raise NotImplementedError()

# 이후 창(Window) 추가 → 인터페이스 변경
class AbstractFactoryV2(AbstractFactory):
    def create_window(self):
        raise NotImplementedError()

📛 문제점: 인터페이스 변경이 연쇄적으로 영향을 미쳐 기존 구현체까지 수정 필요

개선된 예시 (인터페이스 최소화 & 분리)
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
# 역할 별로 인터페이스 분리
class ButtonFactory:
    def create_button(self):
        raise NotImplementedError()

class WindowFactory:
    def create_window(self):
        raise NotImplementedError()

# 필요 시 조합
class UIFactory(ButtonFactory, WindowFactory):
    pass

✅ 각 기능이 독립된 인터페이스로 구성되어 있어 제품군 확장 시 영향 최소화

테스트 전략

전략설명장점
Mock 팩토리 사용테스트 전용 팩토리 구현으로 실제 제품 대신 Mock 객체 생성단위 테스트 간소화, 외부 의존 제거
DI 기반 팩토리 주입팩토리를 외부에서 주입받아 테스트 시 원하는 객체 생성테스트 유연성 향상, 의존성 제어 가능
팩토리 인터페이스 추상화테스트 대상이 인터페이스만 참조테스트 시 다양한 구현 객체로 대체 용이

리팩토링 전략

시나리오전략설명
제품군이 많아짐서브팩토리 분리제품 계열별로 인터페이스를 나누고 소형 팩토리로 분리
생성 메서드 중복 증가공통 로직 유틸리티화생성 로직 공통화 또는 헬퍼 함수 추출
팩토리 중복 사용싱글톤 패턴 적용팩토리의 재사용성과 메모리 사용 최적화
팩토리 난립팩토리 레지스트리 패턴 도입팩토리들을 Map 등으로 관리하여 팩토리 폭주 방지

패턴 확장 적용 시 고려 사항

고려 항목설명주의점
서비스 구성 전략팩토리 기반으로 모듈 분리 가능모듈 경계 명확히, 경합 상황 방지
제품군 확장 전략기존 인터페이스 유지하면서 새로운 제품군 추가SRP 위반하지 않도록 인터페이스 분리 고려
상호 운용성여러 제품군 조합의 인터페이스 통합 여부 판단공통 추상화 계층 정의 필요
제품군 제거 전략사용되지 않는 제품군 관리의존도 파악 및 자동화 도구 활용 (e.g., 코드 커버리지 분석)

최적화하기 위한 고려사항 및 주의점

구분항목설명권장사항
설계 및 구현인터페이스 설계확장성 있는 구조로 설계되어야 하며, 불필요한 메서드 추가는 피해야 함파라미터화된 팩토리 적용, 인터페이스 최소화
팩토리 재사용 구조팩토리를 반복 생성하면 성능 저하와 메모리 낭비 발생싱글톤 또는 DI 컨테이너를 통한 공유 관리
코드 반복 최소화제품군이 많을수록 유사 코드가 많아짐제네릭, 템플릿, 코드 생성기 활용
실행 성능지연 초기화무거운 객체를 불필요하게 즉시 생성하면 응답 지연, 자원 낭비 초래프록시 패턴과 결합한 Lazy Loading
객체 풀링빈번히 재사용되는 객체는 매번 생성하지 않고 재사용 가능Object Pool 패턴 도입
조건 분기 최소화팩토리 선택 로직이 많아질수록 if/switch 가 복잡해짐맵 기반 룩업 테이블 적용 (Key → 팩토리)
팩토리 캐싱동일한 팩토리를 매번 생성하면 리소스 낭비팩토리 인스턴스 캐시 저장 또는 DI 주입
메모리 관리객체 수명 주기 관리생성된 객체가 참조되지 않고 남아 있으면 GC 부하 및 누수 가능성 있음약한 참조 (WeakRef), 명확한 범위 지정, 명시적 해제
불필요 객체 생성 방지모든 제품을 사전에 만들 경우 메모리 낭비필요 시점에만 생성 (On-demand)
GC/메모리 누수 대응팩토리나 제품이 불필요하게 메모리에 남아 있으면 리소스 부족으로 이어질 수 있음주기적 정리, 팩토리 수명주기 모니터링
확장성런타임 팩토리 변경동적 로딩, 설정 파일 기반으로 팩토리를 교체해야 할 수 있음Reflection, 플러그인 아키텍처, 전략 패턴 적용
병렬 처리 지원멀티스레드 환경에서는 상태 공유가 위험할 수 있음불변 객체 사용, ThreadLocal 팩토리 활용
분산 시스템 대응팩토리 인스턴스를 서비스 별로 분산해야 하는 요구 발생서비스 레지스트리와 연계한 팩토리 분산 설계
유지보수 및 모니터링사용 패턴 추적 및 성능 테스트팩토리 변경이 전체 시스템 성능에 영향을 줄 수 있으므로 사전 검증과 추적 필요팩토리 단위 성능 로깅, 사용 메트릭 수집
클래스 증가 관리제품/팩토리 수에 따라 클래스 수 급증 → 관리 부담구조적 패키징, 코드 생성기 사용, 문서화
디버깅 및 테스트 용이성테스트용 객체 생성이 어려울 수 있음Mock 팩토리 또는 테스트 전용 팩토리 도입

주제와 관련하여 주목할 내용

카테고리주제항목설명
설계 원칙 및 아키텍처객체 생성 원칙생성 로직 캡슐화객체 생성 책임 분리로 유연성과 재사용성 향상
DIP / OCP 적용느슨한 결합 / 확장성의존성 역전 및 확장 가능한 구조 설계 가능
SRP 적용단일 책임 부여팩토리는 제품 생성에만 집중
플랫폼 독립성인터페이스 기반 설계플랫폼에 의존하지 않는 구조 실현 가능
레이어드 아키텍처계층 기반 배치팩토리를 계층별로 구성해 역할 분리
디자인 일관성UI 구성요소 일관성 유지Theme 기반 UI 구성 시 효과적
구현 전략인터페이스 기반추상 팩토리제품군 생성 인터페이스 제공
클래스 기반구체 팩토리실제 객체 생성 로직 구현
팩토리 셀렉터런타임 팩토리 결정조건 (운영체제, 지역 등) 에 따라 동적 선택
DI 통합DI/IoC 자동 주입프레임워크가 팩토리를 주입하여 관리
제네릭 팩토리타입 안전성 보장컴파일 타임 타입 체크 가능 (Java, C# 등)
리플렉션 활용런타임 동적 생성팩토리를 리플렉션 기반으로 동적 구성
성능 최적화Lazy Initialization지연 초기화객체가 실제 필요할 때 생성
Object Pooling객체 재사용비용이 큰 객체 재활용으로 메모리 절약
Thread-safe Factory스레드 안전성멀티스레드 환경에서 안전한 객체 생성 보장
Async Factory비동기 팩토리 구현async/await, Future 기반 처리
JIT 최적화 설계인라인화 고려JIT 최적화를 고려한 팩토리 구성
테스트 전략Mock Factory테스트용 객체 생성테스트 대상 대체용 객체 구성
Test DoublesStub / Spy 등다양한 유형의 테스트 더블 지원
통합 테스트 전략실환경 시뮬레이션 테스트실제 제품군/팩토리 기반 테스트 수행
현대적 응용마이크로서비스Service Factory서비스 단위 객체를 생성하는 팩토리
클라우드 환경Provider Factory / Dynamic Loading클라우드 제공업체별 객체 생성 및 런타임 팩토리 로딩
UI/UXTheme Factory테마 기반 UI 요소 일관 생성
디자인 시스템컴포넌트 일관성 유지UI 구성요소 표준화 및 확장성 확보
보안Secure Factory권한 기반 객체 생성 제어
프레임워크 통합Spring,.NET 등DI 컨테이너와 팩토리 자동 연계
프로그래밍 패러다임함수형 프로그래밍고차 함수 기반 팩토리클로저와 커링을 활용한 팩토리 생성
리액티브 프로그래밍스트림 기반 팩토리RxJava, Reactor 기반으로 구성
불변성Immutable 객체 팩토리불변 객체 생성을 위한 빌더 조합
적용 사례GUI 컴포넌트운영체제별 UI 구성OS 별 GUI 를 각각 생성
DB 연결DB 별 커넥션 객체 생성DB 종류에 따라 팩토리 분리 설계
테스트 / 모킹Mock / Stub 팩토리테스트 편의성을 위한 구조
제품군 간 호환성 제어제한된 조합만 허용동일 제품군끼리만 상호작용 가능하게 설계

반드시 학습해야할 내용

핵심 개념 및 구조

카테고리주제항목설명
기본 개념Abstract Factory객체군 생성 인터페이스관련 제품군을 생성하기 위한 추상 인터페이스
Concrete Factory구체 팩토리 구현특정 제품군을 실제로 생성하는 클래스
Product Family제품군 개념함께 사용되는 객체들의 논리적 집합
Client클라이언트 역할추상 인터페이스를 통해 객체를 생성하고 사용하는 코드
Factory Method단일 제품군 생성Abstract Factory 의 하위/보완 패턴
Builder복합 객체 생성 패턴생성 과정 단계 분리, Abstract Factory 와 조합 가능
Singleton인스턴스 제어팩토리 인스턴스의 유일성 보장
Factory Registry팩토리 관리 체계다양한 팩토리를 중앙화하여 등록/선택 가능

객체지향 및 설계 원칙

카테고리주제항목설명
객체지향 설계SOLID 원칙DIP, OCP 등추상화, 확장성, 변경 최소화 등 핵심 설계 원칙
의존성 역전 / 의존성 주입DI, IoC추상화 중심 의존성 관리 방식
인터페이스 설계인터페이스 분리, 일관성변경 최소화, 확장 용이성 확보
느슨한 결합 & 높은 응집도결합도/응집도 관리클래스 간 의존성을 낮추고 내부 일관성을 높임
리플렉션동적 객체 생성런타임 팩토리 생성/바인딩 전략에 활용 가능

구현 전략 및 최적화

카테고리주제항목설명
객체 수명/리소스Lazy Initialization지연 초기화필요한 시점에 객체를 생성하여 메모리 효율화
Object Pooling객체 재사용비용 높은 객체에 대해 풀링 전략 적용
동시성 환경Thread-Safe Factory스레드 안전성 보장멀티스레드 환경에서 안전한 객체 생성
비동기 처리Async Factory비동기 생성 처리async/await, CompletableFuture
성능 최적화GC 최적화가비지 컬렉션 이해객체 수명 주기와 메모리 회수 전략 연계

테스트 및 품질 전략

카테고리주제항목설명
테스트 전략Mock Factory테스트용 팩토리 구성실제 객체 대신 사용할 가짜 객체 생성
Test DoublesStub, Spy, Fake 등다양한 테스트 더블 유형 적용
TDD 적용 전략테스트 우선 개발 방식팩토리 기반 객체 생성을 테스트 주도 방식으로 설계
통합 테스트실제 의존성 기반 테스트팩토리 패턴을 통해 구성된 전체 시스템 테스트

패턴 조합 및 아키텍처 연계

카테고리주제항목설명
패턴 연계Strategy Pattern런타임 팩토리 선택 전략클라이언트에서 전략적으로 팩토리 선택
Composite Pattern제품 계층 구조 구현제품군이 트리 구조일 때 조합 사용
Prototype Pattern객체 복제 기반 생성기존 객체 복사를 통한 새로운 제품 생성
아키텍처 설계Layered Architecture계층화된 설계 구조 적용추상 팩토리를 각 계층에 배치 가능
Plugin Architecture기능 확장 유연성 확보팩토리를 플러그인 형태로 등록/교체 가능
프레임워크 통합Spring, Guice,.NET CoreDI 기반 객체 생성 관리프레임워크 DI 컨테이너와 팩토리 통합
플랫폼 전략Cross-platform클라이언트 코드 불변성인터페이스 기반 구현으로 플랫폼 독립성 확보

학습 및 실무 고려사항

카테고리주제항목설명
설계 원칙확장성제품군 확장 방식새로운 객체군을 쉽게 추가 가능해야 함
유지보수성변경 최소화 전략클라이언트 수정 없이 내부 확장 가능
실무 비용 분석초기 구현 비용도입 전 분석 요소클래스 수 증가, 설계 복잡도 고려
코드 복잡성 관리모듈화구성요소 수 관리클래스/팩토리 수 증가를 분리/캡슐화로 관리
도구 활용코드 생성기, 플러그인자동 팩토리 생성 등코드 템플릿/도구 활용으로 생산성 향상

용어 정리

카테고리용어설명
패턴 구성요소Abstract Factory관련 제품군을 생성하기 위한 추상 인터페이스 또는 클래스
Concrete Factory특정 제품군 생성을 실제로 구현한 클래스
Abstract Product제품군 내 객체들의 공통 인터페이스
Concrete Product실제로 생성되는 구체 객체, Abstract Product 구현체
Client추상 인터페이스를 통해 제품을 생성하고 사용하는 비즈니스 로직 주체
Product Family함께 사용되도록 설계된 관련 객체들의 집합 (예: MacButton, MacCheckbox)
FactoryProducer / FactoryProvider조건에 따라 팩토리를 선택하거나 제공하는 클래스 또는 유틸
Factory Method객체 생성을 캡슐화한 메서드, Abstract Factory 와 함께 사용 가능
Builder복잡한 객체 생성을 위한 패턴, Abstract Factory 와 병행 사용 가능
Singleton팩토리 인스턴스의 유일성 보장을 위한 보조 패턴
설계 원칙DIP (Dependency Inversion Principle)고수준 모듈이 저수준 모듈에 의존하지 않고 추상화에 의존해야 한다는 원칙
OCP (Open/Closed Principle)소프트웨어 요소는 확장에는 열려 있고 변경에는 닫혀 있어야 함
Loose Coupling (느슨한 결합)클래스 간 의존성을 줄이는 설계 방식
High Cohesion (높은 응집도)모듈이나 클래스 내 구성 요소들이 밀접하게 관련된 상태
Runtime Binding실행 시점에 실제 사용할 구체 팩토리나 제품을 결정하는 방식
Cross-Platform다양한 플랫폼에서 공통 코드로 작동하는 소프트웨어 특성
구현 및 기술 전략DI (Dependency Injection)외부에서 의존 객체를 주입받는 방식, 테스트 용이성 향상
Mock Object테스트 시 실제 객체를 대신하는 시뮬레이션 객체
Factory Registry다양한 팩토리를 등록하고 관리하는 중앙 시스템
Lazy Initialization객체가 필요할 때까지 생성을 지연하는 기법
Service Locator런타임에 필요한 서비스를 찾아서 제공하는 구조
SPI (Service Provider Interface)외부 모듈을 통해 팩토리 구현체를 교체할 수 있도록 하는 아키텍처 방식
Prototype객체를 복제하여 새로운 인스턴스를 생성하는 패턴
일반 개념/기초 용어Concrete Class추상 클래스나 인터페이스를 실제로 구현한 클래스
Polymorphism (다형성)동일한 인터페이스로 다양한 구현체를 사용할 수 있는 객체지향 특징
GoF (Gang of Four)디자인 패턴 서적을 집필한 4 인의 저자 (Erich Gamma 외)
Creational Pattern (생성 패턴)객체 생성과 관련된 디자인 패턴의 분류군 (Abstract Factory, Builder 등 포함)

참고 및 출처