기술(Tech, IT)/알고리즘(Algorithm)

[Algorithm] Factory Method Pattern (팩토리 메소드 패턴)

Daniel803 2024. 5. 5. 04:28

Factory Method Pattern은 객체 지향 프로그래밍에서 사용되는 창조적인 디자인 패턴이다. 이 패턴은 객체의 인스턴스화 프로세스를 서브 클래스에 위임하는 방법을 제공해 애플리케이션 아키텍처의 유연성을 높이고 분리할 수 있도록 한다. 이 패턴은 런타임까지 객체의 정확한 유형과 종속성을 확인할 수 없을 때 유용하다.

 

  • 주요 개념
    • Creator Classes
      : 객체를 생성하는 메서드를 선언하는 추상 클래스다. Factory Method 라고도 하는 이 메소드는 제품 클래스의 객체(product class)의 객체를 반환하기 위한 것이다.
    • Concrete Creators
      : creator class를 상속하고 Factory Method를 재정의해 특정 product의 인스턴스를 반환하는 클래스다.
    • Product Classes
      : Factory Method가 생성할 객체를 정의하는 인터페이스 또는 추상 클래스다.
    • Concrete Products
      : product 인터페이스를 구현하거나 추상 product 클래스를 확장하는 클래스다. 각 concrete creator는 해당 concrete producct를 생성한다.
  • 작동 방식
    • Factory Method
      : 이 메소드는 기본 creator class에서 추상적(abstract)이며 concrete creator 클래스에 의해 재정의된다. 특정 유형의 새 인스턴스를 반환하는 역할을 한다.
    • Decoupling
      : creator는 생성하는 concrete product와 분리되어 있으므로 코드가 인퍼테이스를 통해서만 product를 처리한다. 따라서 concrete creator class의 구현에서 product class 이름은 분리할 수 있다.
    • Flexibility and Scalability (유연성 및 확장성)
      : 기존 클라이언트 코드를 변경하지 않고도 새로운 유형의 제품을 추가할 수 있어 확장성과 유지 관리가 용이하다.

아래 예시를 살펴보자.

  • Java
    : 이 예제에서 'NotificationCreator' 클래스는 특정 유형의 알림을 생성하기 위해 'EmailNotificationCreator'와 'SMSNotificationCreator'에서 다르게 구현되는 Factory Method인 createNotification()을 정의한다. 이 설정을 통해 클라이언트 애플리케이션은 런타임에 사용되는 구체적인 creator class에 따라 객체를 생성하는 동시에 작업을 위해 일반 'Notification' 인터페이스로 작업할 수 있다. 이는 Factory Method Pattern이 제공하는 유연성과 분리성을 보여준다.
// Product interface
interface Notification {
    void notifyUser();
}

// Concrete Products
class EmailNotification implements Notification {
    public void notifyUser() {
        System.out.println("Sending an email notification.");
    }
}

class SMSNotification implements Notification {
    public void notifyUser() {
        System.out.println("Sending an SMS notification.");
    }
}

// Creator class
abstract class NotificationCreator {
    public abstract Notification createNotification();
}

// Concrete Creators
class EmailNotificationCreator extends NotificationCreator {
    @Override
    public Notification createNotification() {
        return new EmailNotification();
    }
}

class SMSNotificationCreator extends NotificationCreator {
    @Override
    public Notification createNotification() {
        return new SMSNotification();
    }
}

// Client code
public class Client {
    public static void main(String[] args) {
        NotificationCreator creator = new EmailNotificationCreator();
        Notification notification = creator.createNotification();
        notification.notifyUser();
    }
}

 

  • C++
    : 하기 예제는 소프트웨어 애플리케이션이 사용자의 선택에 따라 다양한 유형의 문서를 생성해야하는 시나리오를 보여준다. 텍스트 문서와 그림 문서 등 다양한 유형의 문서를 처리할 수 있는 문서 편집기를 생각해보자. 다음은 Factory Method Pattern을 사용해 이러한 다양한 유형의 문서 생성을 관리하는 방법이다.
  1. 제품 인터페이스 정의
    : 먼저 일반적인 문서 유형을 나타내는 추상적인 베이스 클래스를 정의한다. 각 특정 유형은 이 클래스에서 상속된다.
    class Document {
    public:
        virtual void Open() = 0;
        virtual void Close() = 0;
        virtual ~Document() {}
    };​
  2. Concrete Product 생성
    : 'Document' 클래스의 구체적인 구현이다.
    class TextDocument : public Document {
    public:
        void Open() override {
            std::cout << "Opening a text document." << std::endl;
        }
        void Close() override {
            std::cout << "Closing a text document." << std::endl;
        }
    };
    
    class DrawingDocument : public Document {
    public:
        void Open() override {
            std::cout << "Opening a drawing document." << std::endl;
        }
        void Close() override {
            std::cout << "Closing a drawing document." << std::endl;
        }
    };​
  3. Creator Class 정의
    : 아래 abstract class는 'Document' 유형을 반환하는 Factory Method를 선언한다.
    class Application {
    public:
        virtual Document* CreateDocument() = 0;
        virtual ~Application() {}
    };​
  4. Concrete Creator Class 구현
    : 각 class는 특정 유형의 문서를 생성한다.
    class TextApplication : public Application {
    public:
        Document* CreateDocument() override {
            return new TextDocument();
        }
    };
    
    class DrawingApplication : public Application {
    public:
        Document* CreateDocument() override {
            return new DrawingDocument();
        }
    };​
  5. Client Code
    : client code는 이제 위 class들을 사용해 해당 문서의 유형을 알지 못해도 생성하고 관리가 가능하다.
    int main() {
        Application* app = new TextApplication();
        Document* doc = app->CreateDocument();
        doc->Open();
        doc->Close();
    
        delete doc;
        delete app;
    
        app = new DrawingApplication();
        doc = app->CreateDocument();
        doc->Open();
        doc->Close();
    
        delete doc;
        delete app;
    
        return 0;
    }
  6. 이 예제에서 Application class가 creator 이며, 특정 유형의 문서를 생성하기 위해 TextApplication 및 DrawingApplication에서 구현하는 CreateDocument() 메소드를 정의한다. client code는 Document 인터페이스를 통해 문서와 상호 작용하므로 Document의 구체적인 구현과 분리된 상태를 유지한다. 이 설계 덕분에 기존 코드에 영향을 주지 않고 새 문서 유형을 더 쉽게 추가할 수 있다.