| 일 | 월 | 화 | 수 | 목 | 금 | 토 |
|---|---|---|---|---|---|---|
| 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 |
Tags
- Lock-free Stack
- 강한 연결 요소
- Spin Lock
- 최소 공통 조상
- 그래프 탐색
- Prisma
- R 그래프
- 벨만-포드
- 비트마스킹
- Delete
- DP
- localstorage
- Github
- Binary Lifting
- PROJECT
- trie
- 이분 탐색
- 비트필드를 이용한 dp
- map
- SCC
- 자바스크립트
- 트라이
- 게임 서버 아키텍처
- ccw 알고리즘
- JavaScript
- Express.js
- Strongly Connected Component
- 2-SAT
- reference counting
- Behavior Design Pattern
Archives
- Today
- Total
dh_0e
[Design Pattern] Creational Design Patterns III (Abstract Factory, Builder, Prototype Pattern) 본문
Software Analysis & Design/Design Patterns
[Design Pattern] Creational Design Patterns III (Abstract Factory, Builder, Prototype Pattern)
dh_0e 2025. 11. 4. 10:59
Abstract Factory Pattern (추상 팩토리 패턴)
- 연관 있는 여러 객체(제품) 군(family)의 생성을 추상화한 생성 패턴
- 팩토리 메서드 패턴이 단일 객체 생성을 추상화하는 반면, 추상 팩토리 패턴은 관련 있는 여러 객체(제품)들의 일관된 생성을 추상화함
- ex) {Chair, Sofa, CoffeeTable}에 대해 스타일 별로 각 제품을 일관되게 생성해야 함

- 새로운 스타일의 제품 군이 추가되더라도 기존 코드를 변경하지 않고 추가할 수 있어야 합니다.
- 제품별로 인터페이스를 추상화하고, 모든 추상 제품에 대한 생성 메서드를 가지는 팩토리로 추상화합니다.
- 각 서브 팩토리에서는 스타일에 일관된 제품군이 생성될 수 있도록 생성 메서드를 구현합니다.


ex) Window와 Mac OS의 Cross-platform GUI 예시

// 1. 버튼 인터페이스
interface Button {
void click();
}
// 2. 체크박스 인터페이스
interface Checkbox {
void check();
}
// --- Windows 스타일 부품들 ---
class WindowsButton implements Button {
public void click() { System.out.println("Windows 버튼 클릭"); }
}
class WindowsCheckbox implements Checkbox {
public void check() { System.out.println("Windows 체크박스 체크"); }
}
// --- Mac 스타일 부품들 ---
class MacButton implements Button {
public void click() { System.out.println("Mac 버튼 클릭"); }
}
class MacCheckbox implements Checkbox {
public void check() { System.out.println("Mac 체크박스 체크"); }
}
interface GUIFactory {
Button createButton();
Checkbox createCheckbox();
}
// Windows 전용 공장 (윈도우 부품만 나옴)
class WindowsFactory implements GUIFactory {
public Button createButton() { return new WindowsButton(); }
public Checkbox createCheckbox() { return new WindowsCheckbox(); }
}
// Mac 전용 공장 (맥 부품만 나옴)
class MacFactory implements GUIFactory {
public Button createButton() { return new MacButton(); }
public Checkbox createCheckbox() { return new MacCheckbox(); }
}
class Application {
private Button button;
private Checkbox checkbox;
// 생성자에서 공장을 받아서 부품을 조립
public Application(GUIFactory factory) {
button = factory.createButton();
checkbox = factory.createCheckbox();
}
public void paint() {
button.click();
checkbox.check();
}
}
public class Main {
public static void main(String[] args) {
// 설정에 따라 공장만 갈아끼우면 전체 테마가 바뀜!
GUIFactory factory;
String osName = "mac"; // 만약 이 값이 "windows"라면?
if (osName.equals("mac")) {
factory = new MacFactory();
} else {
factory = new WindowsFactory();
}
// 앱 실행 (앱은 이게 윈도우인지 맥인지 모름, 그냥 인터페이스만 믿고 씀)
Application app = new Application(factory);
app.paint();
}
}
추상 팩토리 패턴 장단점
- 장점
- 객체를 생성하는 코드를 분리하여 클라이언트 코드와 결합도를 낮출 수 있음
- 제품 군(family)을 쉽게 대체할 수 있음
- 단일 책임 원칙(SRP)및 개방-폐쇄 원칙(OCP)을 준수함
- ex) 위 Cross-platform 예시에서 Linux에 관한 Factory를 추가해도 코드를 추가하지 않음
- ex) 위 Cross-platform 예시에서 Linux에 관한 Factory를 추가해도 코드를 추가하지 않음
- 단점
- 제품, 팩토리들을 모두 구현해주어야 하므로 코드 구조 복잡성이 높음 (팩토리 메서드 패턴과 동일)
- 새로운 제품 추가 시 모든 팩토리 구현 로직에 새로운 생성 함수가 추가되어야 함
| 구분 | 팩토리 메서드 | 추상 팩토리 |
| 목적 | 생성 책임을 서브클래스의 메서드로 위임 | 관련 제품군을 한꺼번에 일관되게 생성 |
| 범위 | 단일 제품 계열의 한 타입 | 여러 제품 타입으로 이뤄진 제품군 |
| 구조 | Creator + factoryMethod() 1개 Product 인터페이스 | AbstractFactory + 여러 makeXxx() + 여러 Product 인터페이스 |
| 확장 | 새 제품 타입마다 Creator 서브클래스 추가 | 새 제품군마다 ConcreteFactory와 구현 세트 추가 |
| 교체 단위 | 제품 타입 단위 교체 | 제품군(테마/플랫폼) 단위 교체 |
| 쓰는 때 | 생성 방법을 하위가 결정해야 할 때 | 버튼·체크박스처럼 함께 맞춰 바꿔야 할 타입들이 있을 때 |
- 팩토리 메서드: 공장 → 제품(한 계열)
- 추상 팩토리: 공장 → 연관된 제품들(제품 패밀리)
Builder Pattern (빌더 패턴)
- 복잡한 객체 생성 과정과 표현 방법을 분리하여 클라이언트가 다양한 구성을 조합하여 객체를 생성할 수 있도록 하는 생성 패턴
- 객체 생성 및 초기화 시 멤버 변수가 많을 경우, 모든 멤버 변수를 생성자에 넣는 것이 비효율적일 수 있음
- 특정 개체 생성 시 모든 멤버 변수의 값이 쓰이지 않는다면 더욱 그럼

- 클래스 내에서 객체 생성에 관련된 코드를 빼내서 Builder라는 별도의 객체로 옮기고, Builder를 이용하여 값을 설정할 수 있게 하는 방법


ex) Car & CarManual의 Builder pattern

Builder Pattern 구현


Builder Pattern 장단점
- 장점
- 순차적 조립: 구성하기 복잡한 객체를 순차적으로 만들 수 있음
- 생성 과정 추상화: 복잡한 객체를 만드는 구체적인 과정을 숨길 수 있음
- 재사용성: 동일한 프로세스를 통해 구성에 따라 다른 객체를 만들 수 있음
- 신뢰성: 불완전한 객체를 사용하지 못하도록 방지 할 수 있음
- 단점
- 선행적으로 빌더 객체를 먼저 만들어야 원하는 객체를 만들 수 있음
- 객체 생성을 위한 코드보다 구조적으로 복잡해질 수 있음
- 간단한 객체는 생성자를 통해서 만드는 것이 더 좋음
Prototype Pattern (프로토타입 패턴)
- 기존의 객체를 복제(clone)하여 새로운 객체를 만드는 생성 패턴
- 클래스에 의존하지 않으면서 기존 객체 복사 가능
- 원형(prototypical) 인스턴스를 사용하여 생성할 객체의 종류를 명시, 견본을 복사해서 새로운 객체 생성
- 객체 생성/복사가 까다로운 상황(ex. private/protected 멤버 변수 때문에 클래스 외부에서 복사 불가 / 객체 생성에 많은 리소스가 필요한 경우)에 유용

Cloneable 인터페이스 in Java
- 자바에서 $Objects.clone()$ 메소드는 인스턴스 객체의 복제를 위한 메소드
- 해당 인스턴스를 복제하여 새로운 인스턴스를 생성해 레퍼런스를 반환
- clone() 메소드를 사용하려면 Cloneable 인터페이스를 implements 하고 clone() 메소드를 오버라이딩 해주어야 함 (Prototype 인터페이스 역할)

- Shallow copy(얕은 복사): 객체의 필드 값만 복사 (포인터 값 복사)

- Deep cop(깊은 복사): 독립적인 복제 객체로 모든 값을 복사 (일반 복사)

ex) Prototype pattern with shallow copy


- 만약 deep clone이 되게 복사를 하고 싶으면 clone() 메소드에서 deep copy 될 수 있도록 변경해 주면 됨

Prototype pattern의 장단점
- 장점
- 간단한 과정: 객체를 생성하는 복잡한 과정을 피할 수 있음 (생성자 호출 대신 복제)
- 빠른 속도: 객체를 복제하는 것이 객체를 새로 생성하는 것보다 일반적으로 더 빠름
- 런타임에 객체의 타입을 동적으로 변경하거나 새로운 객체 유형을 추가하는 데 유용함
- new Goblin()이 여러 개 박혀있는 코드에서 Dragon으로 소환수를 바꾸고자 할 때, 원래는 코드를 모두 수정해야 함
- >> prototype으로 런타임 도중에 쉽게 바꿀 수 있음
Monster prototype = new Goblin(); // 샘플 하나 들고 있음
Monster spawn() {
return prototype.clone(); // 샘플을 복사해서 소환
}
prototype = new Dragon(); // 런타임에 바꿔치기
register("slime", new Slime()); // 새 프로토타입 추가
Monster m = create("slime"); // 등록된 샘플을 복사해서 생성
- 단점
- 복제 과정 혼잡: 객체가 복잡한 구조를 가지고 있거나 객체 간에 서로 참조가 있을 때, 객체 복제 과정(특히 깊은 복사 구현)이 복잡해질 수 있음
- 메모리 사용량 문제: 프로토타입 패턴은 객체를 복제하므로 남발 시 메모리 사용량이 늘어날 수 있음
- 객체 복제로 인해 객체의 상태 관리가 어려워질 수 있음
