백엔드 개발자

[디자인패턴] 생성패턴 본문

카테고리 없음

[디자인패턴] 생성패턴

임잠탱 2025. 1. 23. 20:40

생성패턴이란?

생성패턴은 객체의 생성 과정을 추상화하여 객체 생성의 유연성과 재사용성을 높이는 데 도움을 주는 디자인 패턴이다.

잘 알려진 다섯가지 패턴(싱글톤 패턴, 팩터리 메서드 패턴, 추상 팩토리 패턴, 빌더 패턴, 프로토타입 패턴)에 대해 간단히 살펴보자.

1. 싱글톤 패턴 (Singleton Pattern)

  • 특정 클래스의 인스턴스가 오직 하나만 생성되도록 보장하는 패턴.

구현 방법:

public class Singleton {
    private static Singleton instance;

    private Singleton() {}
    public static Singleton getInstance() {
        if (instance == null) {
            instance = new Singleton();
        }
        return instance;
    }

생성자를 private으로 선언하여 외부에서 호출하지 못하도록 막았다.
getInstance()라는 메서드를 통해 전역으로 선언된 Singleton instance를 가져올 수 있는데,
첫 호출에서 instance가 null인 경우 객체를 생성해주고, 이후 하나의 instance로만 재사용을 한다.

 

특징:

  • 인스턴스가 하나만 존재함을 보장.
  • 전역 접근점을 제공.
  • 멀티스레드 환경에서는 동기화 고려 필요. (상태를 변화시키거나 동시에 사용되면 문제가 있을 경우)

언제 쓸 수 있을까?:

  • 데이터베이스 연결 풀 (Connection Pool)
    • DB 연결 관리를 위해 하나의 인스턴스를 유지하며, 여러 요청에서 공유.
    • 예: javax.sql.DataSource 구현체.
  • 로깅 시스템 (Logging)
    • 전역적으로 접근해야 하는 로깅 기능.
    • 예: java.util.logging.Logger 또는 Log4j.
  • 설정 관리 (Configuration Management)
    • 애플리케이션 전반에 걸쳐 필요한 설정 값을 중앙에서 관리.
    • 예: Spring의 Environment 클래스.
      또한, 스프링 빈은 기본적으로 싱글톤으로 제공이 된다.

2. 팩토리 메서드 패턴 (Factory Method Pattern)

  • 객체 생성을 서브클래스에서 정의하도록 하고, 생성 로직을 캡슐화하는 패턴.
  • 객체 생성을 위한 공통 인터페이스를 제공하여 유연한 설계 가능.

구현 방법:

//Product 인터페이스
interface Product {
    void use();
}

//A 타입의 Product
class ConcreteProductA implements Product {
    public void use() {
        System.out.println("Using Product A");
    }
}

//B 타입의 Product
class ConcreteProductB implements Product {
    public void use() {
        System.out.println("Using Product B");
    }
}

//객체 생성을 위한 서브클래스
class ProductFactory {
    public static Product createProduct(String type) {
        if (type.equals("A")) {
            return new ConcreteProductA();
        } else if (type.equals("B")) {
            return new ConcreteProductB();
        }
        throw new IllegalArgumentException("Unknown product type");
    }
}

특징:

  • 객체 생성의 로직을 한 곳에 집중.
  • 확장성 용이, 클라이언트 코드 변경 최소화.

언제 쓸 수 있을까?:

  • UI 요소 생성 (Cross-Platform UI Frameworks)
    • 동일한 인터페이스를 통해 플랫폼별 UI 구성 요소 생성.
    • 예: JavaFX, Android의 LayoutInflater.
  • 데이터 변환기 (Parsers)
    • 다양한 파일 형식(XML, JSON 등)에 대한 파서 객체를 생성.
    • 예: Jackson의 ObjectMapper, JSON 파서.
  • 의존성 주입 프레임워크 (Dependency Injection)
    • 객체 생성을 제어하며, 필요 시 인스턴스를 반환.
    • 예: Spring의 BeanFactory.

3. 추상 팩토리 패턴 (Abstract Factory Pattern)

  • 관련 객체의 **군(군집)**을 생성하기 위한 패턴.
  • 서로 관련된 객체를 생성할 때 일관성을 유지.

구현 방법:

interface GUIFactory {
    Button createButton();
    Checkbox createCheckbox();
}

class WinFactory implements GUIFactory {
    public Button createButton() {
        return new WinButton();
    }
    public Checkbox createCheckbox() {
        return new WinCheckbox();
    }
}

class MacFactory implements GUIFactory {
    public Button createButton() {
        return new MacButton();
    }
    public Checkbox createCheckbox() {
        return new MacCheckbox();
    }
}

특징:

  • 제품군을 일관되게 생성.
  • 클라이언트는 구체적인 클래스를 몰라도 됨.

언제 쓸 수 있을까?:

  • 의존성 관리 시스템 (Spring Framework BeanFactory)
    • 클라이언트 코드에서 구체적인 객체 생성에 대한 의존성을 제거.
  • 데이터베이스 연결 (JDBC Connection Factory)
    • 다양한 데이터베이스(MySQL, Oracle 등)를 위한 팩토리 제공.

4. 빌더 패턴 (Builder Pattern)

  • 복잡한 객체의 생성 과정을 단계적으로 진행할 수 있도록 하는 패턴.
  • 동일한 객체 생성 절차에서 다양한 표현을 제공.

구현 방법:

class Car {
    private String engine;
    private int wheels;

    public Car(String engine, int wheels) {
        this.engine = engine;
        this.wheels = wheels;
    }
}

class CarBuilder {
    private String engine;
    private int wheels;

    public CarBuilder setEngine(String engine) {
        this.engine = engine;
        return this;
    }

    public CarBuilder setWheels(int wheels) {
        this.wheels = wheels;
        return this;
    }

    public Car build() {
        return new Car(engine, wheels);
    }
}

// 체이닝을 통한 연쇄 작업이 가능하게 해준다.
Car car = new CarBuilder().setEngine("V8").setWheels(4).build();

생성자가 Car(int wheel, int gas, int year, int price) 처럼 매개변수가 많을 경우 기억하기 쉽지 않고,
매개변수 순서대로 정확히 입력해야 하는 어려움이 있다. 빌더 패턴은 이런 순서에 상관없이 가능하게 해준다.

 

특징:

  • 생성 과정이 복잡한 객체를 단계적으로 생성.
  • 객체 생성의 가독성과 유지보수성 향상.

언제 쓸 수 있을까?:

  • 복잡한 객체 생성 (HTTP 요청 생성)
    • 옵션이 많은 API 요청 객체를 단계적으로 설정.
    • 예: StringBuilder, java.net.HttpClient.Builder.
  • SQL 쿼리 빌더
    • 동적으로 SQL 쿼리를 조립하기 위해 사용.
    • 예: Hibernate CriteriaBuilder.
  • 게임 개발 (캐릭터 생성기)
    • 캐릭터의 스탯, 장비, 스킬을 조합할 때 사용.

5. 프로토타입 패턴 (Prototype Pattern)

  • 기존 객체를 복제하여 새로운 객체를 생성하는 패턴.
  • 비용이 많이 드는 객체 생성 시 성능 최적화에 유용.

구현 방법:

class Prototype implements Cloneable {
    private String data;

    public Prototype(String data) {
        this.data = data;
    }

    public Prototype clone() throws CloneNotSupportedException {
        return (Prototype) super.clone();
    }

    public String getData() {
        return data;
    }
}

// 사용
Prototype original = new Prototype("Original Data");
Prototype clone = original.clone();

특징:

  • 객체 생성 비용 절감.
  • 복잡한 객체 구조 복제에 적합.

언제 쓸 수 있을까?:

  • 게임 개발 (캐릭터 및 아이템 복제)
    • 기본 캐릭터를 생성 후 일부 속성을 변경해 여러 개의 객체 생성.
  • 문서 편집기 (복사-붙여넣기 기능)
    • 복잡한 문서 객체를 복제하여 빠르게 새 문서 생성.
  • 머신러닝 모델 복제
    • AI 모델을 여러 환경에서 빠르게 복제 및 실험.

Comments