본문 바로가기

c c++ mfc

디자인패턴 - 추상 팩토리 패턴(Abstract Factory Pattern)

반응형

추상 팩토리 패턴(Abstract Factory Pattern)은 객체 생성 패턴 중 하나로, 관련된 객체 군을 생성하는 인터페이스를 제공하는 패턴입니다. 이 패턴은 구체적인 클래스에 의존하지 않고, 인터페이스를 통해 객체를 생성함으로써 코드의 유연성과 확장성을 높입니다.

 

추상팩토리패턴

주요 특징

1. 객체 군 생성: 추상 팩토리 패턴은 관련된 객체를 함께 생성할 수 있도록 해줍니다. 예를 들어, 특정 스타일의 UI 컴포넌트를 생성하는 팩토리를 만들 수 있습니다.

2. 인터페이스 제공: 클라이언트는 구체적인 클래스에 의존하지 않고, 인터페이스를 통해 객체를 요청합니다. 이를 통해 코드의 결합도를 낮출 수 있습니다.

3. 확장성: 새로운 제품군을 추가할 때 기존 코드를 변경하지 않고 새로운 팩토리 클래스를 추가하는 방식으로 확장이 가능합니다.

 

구성 요소

1. 추상 팩토리(Abstract Factory): 객체 생성 메서드의 인터페이스를 정의합니다.
2. 구체 팩토리(Concrete Factory): 추상 팩토리 인터페이스를 구현하여 구체적인 제품 객체를 생성합니다.
3. 추상 제품(Abstract Product): 생성될 객체의 인터페이스를 정의합니다.
4. 구체 제품(Concrete Product): 추상 제품 인터페이스를 구현하는 구체적인 객체입니다.

 

예시코드

#include <iostream>

// 추상 제품 인터페이스
class Button {
public:
    virtual void paint() = 0; // 순수 가상 함수
};

class Checkbox {
public:
    virtual void paint() = 0; // 순수 가상 함수
};

// 구체 제품 클래스
class WindowsButton : public Button {
public:
    void paint() override {
        std::cout << "Windows Button" << std::endl;
    }
};

class WindowsCheckbox : public Checkbox {
public:
    void paint() override {
        std::cout << "Windows Checkbox" << std::endl;
    }
};

// 또 다른 구체 제품 클래스
class MacOSButton : public Button {
public:
    void paint() override {
        std::cout << "MacOS Button" << std::endl;
    }
};

class MacOSCheckbox : public Checkbox {
public:
    void paint() override {
        std::cout << "MacOS Checkbox" << std::endl;
    }
};

// 추상 팩토리 인터페이스
class GUIFactory {
public:
    virtual Button* createButton() = 0; // 순수 가상 함수
    virtual Checkbox* createCheckbox() = 0; // 순수 가상 함수
};

// 구체 팩토리 클래스
class WindowsFactory : public GUIFactory {
public:
    Button* createButton() override {
        return new WindowsButton();
    }

    Checkbox* createCheckbox() override {
        return new WindowsCheckbox();
    }
};

class MacOSFactory : public GUIFactory {
public:
    Button* createButton() override {
        return new MacOSButton();
    }

    Checkbox* createCheckbox() override {
        return new MacOSCheckbox();
    }
};

// 클라이언트 코드
class Application {
private:
    Button* button;
    Checkbox* checkbox;

public:
    Application(GUIFactory* factory) {
        button = factory->createButton();
        checkbox = factory->createCheckbox();
    }

    void paint() {
        button->paint();
        checkbox->paint();
    }
};

// 메인 함수
int main() {
    GUIFactory* factory;

    // Windows 환경에서 실행
    factory = new WindowsFactory();
    Application* app1 = new Application(factory);
    app1->paint();

    delete app1;
    delete factory;

    // MacOS 환경에서 실행
    factory = new MacOSFactory();
    Application* app2 = new Application(factory);
    app2->paint();

    delete app2;
    delete factory;

    return 0;
}

코드 설명

1. 추상 제품 인터페이스: Button과 Checkbox 클래스를 정의하여 각각의 제품이 가져야 할 메서드를 선언합니다.

2. 구체 제품 클래스: WindowsButton, WindowsCheckbox, MacOSButton, MacOSCheckbox 클래스가 각각의 플랫폼에 맞는 버튼과 체크박스를 구현합니다.

3. 추상 팩토리 인터페이스: GUIFactory 클래스는 버튼과 체크박스를 생성하는 메서드를 선언합니다.

4. 구체 팩토리 클래스: WindowsFactory와 MacOSFactory 클래스는 GUIFactory를 구현하여 각 플랫폼에 맞는 버튼과 체크박스를 생성합니다.

5. 클라이언트 코드: Application 클래스는 팩토리를 통해 버튼과 체크박스를 생성하고, paint() 메서드를 호출하여 생성된 UI 요소를 표시합니다.

6. 메인 함수: main 함수에서 WindowsFactory와 MacOSFactory를 사용하여 각각의 UI 요소를 생성하고, 클라이언트 애플리케이션을 실행합니다.

 

이와 같이 추상 팩토리 패턴을 사용하면 클라이언트는 구체적인 클래스에 의존하지 않고, 인터페이스를 통해 객체를 생성할 수 있어 코드의 유연성과 확장성이 높아집니다.

 

실행결과 : 

Windows Button
Windows Checkbox
MacOS Button
MacOS Checkbox

 

실행 과정 설명:

1. Windows 환경에서 실행:
 - WindowsFactory 객체가 생성되고, 이를 사용하여 Application 객체가 생성됩니다.
 - Application 생성자에서 createButton()과 createCheckbox() 메서드를 호출하여 WindowsButton과 WindowsCheckbox 객체가 생성됩니다.
- app1->paint() 호출 시, WindowsButton과 WindowsCheckbox의 paint() 메서드가 각각 호출되어 "Windows Button"과 "Windows Checkbox" 메시지가 출력됩니다.


2. MacOS 환경에서 실행:
- 이후 MacOSFactory 객체가 생성되고, 새로운 Application 객체가 생성됩니다.
- Application 생성자에서 createButton()과 createCheckbox() 메서드를 호출하여 MacOSButton과 MacOSCheckbox 객체가 생성됩니다.
- app2->paint() 호출 시, MacOSButton과 MacOSCheckbox의 paint() 메서드가 각각 호출되어 "MacOS Button"과 "MacOS Checkbox" 메시지가 출력됩니다.

 

예시코드 하나 더 보겠습니다.

#include <iostream>
#include <string>
using namespace std;

// Abstract Product A
class Chair {
public:
    virtual void sitOn() const = 0; // Pure virtual function
    virtual ~Chair() {}
};

// Concrete Product A1
class ModernChair : public Chair {
public:
    void sitOn() const override {
        cout << "Sitting on a modern chair." << endl;
    }
};

// Concrete Product A2
class VictorianChair : public Chair {
public:
    void sitOn() const override {
        cout << "Sitting on a Victorian-style chair." << endl;
    }
};

// Abstract Product B
class Sofa {
public:
    virtual void lieOn() const = 0; // Pure virtual function
    virtual ~Sofa() {}
};

// Concrete Product B1
class ModernSofa : public Sofa {
public:
    void lieOn() const override {
        cout << "Lying on a modern sofa." << endl;
    }
};

// Concrete Product B2
class VictorianSofa : public Sofa {
public:
    void lieOn() const override {
        cout << "Lying on a Victorian-style sofa." << endl;
    }
};

// Abstract Factory
class FurnitureFactory {
public:
    virtual Chair* createChair() const = 0;
    virtual Sofa* createSofa() const = 0;
    virtual ~FurnitureFactory() {}
};

// Concrete Factory 1
class ModernFurnitureFactory : public FurnitureFactory {
public:
    Chair* createChair() const override {
        return new ModernChair();
    }
    Sofa* createSofa() const override {
        return new ModernSofa();
    }
};

// Concrete Factory 2
class VictorianFurnitureFactory : public FurnitureFactory {
public:
    Chair* createChair() const override {
        return new VictorianChair();
    }
    Sofa* createSofa() const override {
        return new VictorianSofa();
    }
};

// Client code
void clientCode(const FurnitureFactory& factory) {
    Chair* chair = factory.createChair();
    Sofa* sofa = factory.createSofa();

    chair->sitOn();
    sofa->lieOn();

    delete chair;
    delete sofa;
}

int main() {
    cout << "Modern furniture factory in use:\n";
    ModernFurnitureFactory modernFactory;
    clientCode(modernFactory);

    cout << "\nVictorian furniture factory in use:\n";
    VictorianFurnitureFactory victorianFactory;
    clientCode(victorianFactory);

    return 0;
}

코드 설명:

  1. 추상 제품 클래스 (ChairSofa):
    • Chair와 Sofa는 추상 제품의 인터페이스를 정의하며, 구체적인 제품들이 이를 구현합니다.
  2. 구체적인 제품 클래스:
    • ModernChair와 VictorianChair: 각각 현대식 의자와 빅토리아 스타일의 의자를 나타냅니다.
    • ModernSofa와 VictorianSofa: 각각 현대식 소파와 빅토리아 스타일 소파를 나타냅니다.
  3. 추상 팩토리 (FurnitureFactory):
    • 서로 관련된 제품(의자와 소파)을 생성할 수 있는 인터페이스를 제공합니다.
    • createChair와 createSofa라는 두 가지 순수 가상 메서드가 포함되어 있습니다.
  4. 구체적인 팩토리 클래스:
    • ModernFurnitureFactory: 현대식 가구(ModernChair, ModernSofa)를 생성합니다.
    • VictorianFurnitureFactory: 빅토리아 스타일 가구(VictorianChair, VictorianSofa)를 생성합니다.
  5. 클라이언트 코드:
    • 클라이언트는 추상 팩토리를 사용하여 제품을 생성합니다. 특정 제품군(Modern 또는 Victorian)에 대한 세부 사항은 팩토리 구현에 캡슐화되어 있습니다.

출력 결과:

프로그램을 실행하면 아래와 같은 출력이 예상됩니다:

Modern furniture factory in use:
Sitting on a modern chair.
Lying on a modern sofa.

Victorian furniture factory in use:
Sitting on a Victorian-style chair.
Lying on a Victorian-style sofa.

 

추상 팩토리 패턴을 사용하면 코드 유지보수에 여러 가지 장점이 있습니다


1. 결합도 감소: 클라이언트 코드가 구체적인 클래스에 의존하지 않고 인터페이스에만 의존하기 때문에, 특정 구현을 변경하더라도 클라이언트 코드에 영향을 주지 않습니다. 이는 코드의 변경과 유지보수를 용이하게 합니다.

2. 확장성: 새로운 제품군이나 구현을 추가할 때, 기존 코드를 수정할 필요 없이 새로운 구체 팩토리 클래스를 추가하면 됩니다. 이는 새로운 기능을 추가할 때 코드의 안정성을 높입니다.

3. 일관성 유지: 같은 제품군 내에서 일관된 방식으로 객체를 생성할 수 있어, 제품 간의 일관성을 유지하는 데 도움이 됩니다. 이는 코드의 이해도를 높이고, 버그 발생 가능성을 줄입니다.

4. 테스트 용이성: 인터페이스를 통해 객체를 생성하기 때문에, 모의 객체(mock object)를 쉽게 사용할 수 있습니다. 이는 유닛 테스트를 작성할 때 유용하여, 테스트 코드의 유지보수성을 높입니다.

5. 코드 가독성 향상: 추상 팩토리 패턴을 사용하면 각 제품의 생성 로직이 명확히 분리되어 있어, 코드의 가독성이 향상됩니다. 이는 다른 개발자가 코드를 이해하고 수정하는 데 도움을 줍니다.

6. 의존성 관리: 객체 생성 로직을 팩토리 클래스에 집중시킴으로써, 의존성 관리를 더 쉽게 할 수 있습니다. 이는 종속성을 명확히 하고, 코드의 구조를 보다 깔끔하게 유지하는 데 기여합니다.

7. 이러한 장점들은 소프트웨어 개발 과정에서 유지보수성과 확장성을 크게 향상시켜, 장기적으로 코드 품질을 높이는 데 기여합니다.

 

이러한 장점들은 소프트웨어 개발 과정에서 유지보수성과 확장성을 크게 향상시켜, 장기적으로 코드 품질을 높이는 데 기여합니다.

 

추상 팩토리 패턴은 여러 관련 객체를 일관되게 생성할 수 있는 유연성과 확장성을 제공하여, 복잡한 시스템에서 일관된 제품군을 관리하는 데 적합합니다. 다른 디자인 패턴들과 비교했을 때, 이러한 장점들은 코드의 유지보수성과 확장성을 높이는 데 크게 기여합니다. 각 패턴의 목적에 따라 적합한 상황이 다르기 때문에, 프로젝트의 요구사항에 따라 적절한 패턴을 선택하는 것이 중요합니다.

반응형