디자인 패턴(생성 패턴) 실제로 어떻게 적용할까 ? (1편)

소일로·2024년 12월 30일
0
post-thumbnail

디자인 패턴: 싱글턴, 팩토리 메소드, 추상 팩토리 패턴

소프트웨어 개발에서 디자인 패턴은 단순히 이론이 아니라 실무에서 문제를 해결하는 실사례들이 많습니다. 이 글에서는 싱글턴(Singleton), 팩토리 메소드(Factory Method), 추상 팩토리(Abstract Factory) 패턴을 실제 애플리케이션에서 어떻게 활용할 수 있는지, 그리고 각각의 장단점과 유의점을 살펴보겠습니다.


1. 싱글턴 패턴 (Singleton Pattern)

언제 사용할까?

  • 애플리케이션에서 하나의 객체만 필요할 때: 설정 관리, 로그 관리, 데이터베이스 연결 등.
  • 리소스를 공유해야 할 때: 동일한 자원을 여러 곳에서 안전하게 사용해야 하는 경우.

실제 적용

  • Spring: 기본적으로 Spring 빈은 싱글턴 스코프입니다. @Bean@Configuration으로 싱글턴 객체를 생성하고 관리합니다.
  • NestJS: @Injectable() 데코레이터를 사용한 서비스 클래스는 기본적으로 싱글턴으로 관리됩니다.

코드 예제 (Java)

public class Singleton {
    private static Singleton instance;

    private Singleton() {}

    public static Singleton getInstance() {
        if (instance == null) {
            synchronized (Singleton.class) {
                if (instance == null) {
                    instance = new Singleton();
                }
            }
        }
        return instance;
    }
}

장점

  • 전역 상태를 관리하기 쉽다.
  • 메모리 낭비를 방지한다.

유의점

  • 테스트가 어렵다 (전역 상태를 가지므로).
  • 다중 스레드 환경에서 동기화 문제를 주의해야 한다.

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

언제 사용할까?

  • 객체 생성 로직을 캡슐화하고 싶을 때: 객체 생성 방식이 변경되더라도 클라이언트 코드를 수정하지 않기 위해.
  • 서브클래스를 통해 객체 생성을 제어하고 싶을 때.

실제 적용

  • Spring: FactoryBean을 사용해 복잡한 객체 생성 로직을 캡슐화합니다.
  • NestJS: 커스텀 프로바이더를 통해 조건에 따라 객체를 생성하거나 주입합니다.

코드 예제 (Java)

abstract class Product {
    public abstract void use();
}

class ConcreteProductA extends Product {
    @Override
    public void use() {
        System.out.println("Using Product A");
    }
}

class ConcreteProductB extends Product {
    @Override
    public void use() {
        System.out.println("Using Product B");
    }
}

abstract class Creator {
    public abstract Product factoryMethod();
}

class ConcreteCreatorA extends Creator {
    @Override
    public Product factoryMethod() {
        return new ConcreteProductA();
    }
}

// 사용
Creator creator = new ConcreteCreatorA();
Product product = creator.factoryMethod();
product.use();

장점

  • 객체 생성 로직을 캡슐화하여 코드 재사용성을 높인다.
  • 클라이언트와 구체적인 클래스 간 결합도를 낮춘다.

유의점

  • 클래스 수가 늘어날 수 있다.
  • 단일 객체 생성만 필요한 경우 과도한 구현이 될 수 있다.

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

언제 사용할까?

  • 여러 관련 객체를 그룹으로 생성해야 할 때: 동일한 제품군의 객체들을 일관되게 생성하기 위해.
  • 플랫폼 독립적인 설계가 필요할 때: 예를 들어, Windows와 MacOS의 UI 컴포넌트를 생성.

실제 적용

  • Spring: AbstractApplicationContext는 다양한 환경(Context)에서 동작하는 객체군을 생성합니다.
  • NestJS: Dynamic Modules를 사용해 모듈 수준에서 설정에 따라 다른 구현체를 제공합니다.

코드 예제 (Java)

interface Button {
    void render();
}

interface Checkbox {
    void render();
}

class WindowsButton implements Button {
    @Override
    public void render() {
        System.out.println("Rendering Windows Button");
    }
}

class MacOSButton implements Button {
    @Override
    public void render() {
        System.out.println("Rendering MacOS Button");
    }
}

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

class WindowsFactory implements GUIFactory {
    @Override
    public Button createButton() {
        return new WindowsButton();
    }

    @Override
    public Checkbox createCheckbox() {
        return new WindowsCheckbox();
    }
}

// 사용
GUIFactory factory = new WindowsFactory();
Button button = factory.createButton();
button.render();

장점

  • 관련 객체 간 일관성을 유지한다.
  • 제품군 전체를 생성하는 로직을 캡슐화한다.

유의점

  • 구현이 복잡하고, 새로운 팩토리 클래스가 필요할 수 있다.
  • 코드 양이 많아질 수 있다.

결론

  • 싱글턴 패턴은 하나의 객체를 전역적으로 공유해야 할 때 적합하며, 전역 상태 관리의 장점과 테스트의 어려움이라는 단점을 모두 고려해야 합니다.
  • 팩토리 메소드 패턴은 객체 생성 과정을 캡슐화하고 확장성을 높이지만, 클래스 구조가 복잡해질 가능성을 유의해야 합니다.
  • 추상 팩토리 패턴은 여러 관련 객체를 그룹으로 생성해야 할 때 적합하며, 복잡한 제품군 설계에서 특히 유용합니다.
profile
백엔드 개발자

0개의 댓글