[디자인패턴] 구조패턴(4) - Composite Pattern (컴포지트 패턴)

Study/Software Architecture · 2023. 5. 25. 17:43

디자인 패턴 총정리 시리즈의 아홉 번째 패턴은 Composite Pattern입니다.

 

소프트웨어를 개발할 때 객체와 그 계층 간의 복잡한 구조를 다뤄야 하는 경우가 있습니다. 이때 Composite 패턴은 각각의 객체나 객체의 그룹을 균일하게 다룰 수 있는 방법을 제공합니다.

 

해당 게시물에서 이러한 Composite 패턴에 대한 간단한 개념과 사용 예시, 장/단점을 알아보겠습니다.

 

1. 개요

Composite 패턴은 구조패턴 중 하나로써 객체를 tree와 같은 구조로 구성하여 전체-부분 계층 구조를 나타내는 디자인 패턴입니다. 해당 패턴은 개별 객체(leaves)와 객체 그룹(composite)을 일관되게 다룰 수 있도록 만들어주기 때문에 복잡한 객체 구조의 상호작용과 제어를 용이하게 해 줍니다.

 

해당 패턴은 Component, Leaf, Composite 3가지 주요 컴포넌트로 구성되어 있습니다.

 

2. 예시

다음은 Composite 패턴을 사용하여 조직의 계층 구조를 나타내는 시나리오를 고려해 보겠습니다. 해당 예시에는 부서(Department)가 있고, 부서는 개별 직원(Employee)과 하위 부서(sub-department)를 포함하고 있습니다.

 

Composite 패턴을 사용하여 부서와 직원을 일관되게 제어할 수 있는 유연한 계층 구조를 구성해 보겠습니다.

 

#include <iostream>
#include <string>
#include <vector>

// Component: Abstract Base Class
class OrganizationComponent {
public:
    virtual void displayInfo() const = 0;
};

// Leaf: Employee
class Employee : public OrganizationComponent {
private:
    std::string name;
    std::string position;

public:
    Employee(const std::string& name, const std::string& position)
        : name(name), position(position) {}

    void displayInfo() const override {
        std::cout << "Employee: " << name << ", Position: " << position << std::endl;
    }
};

// Composite: Department
class Department : public OrganizationComponent {
private:
    std::string name;
    std::vector<OrganizationComponent*> children;

public:
    Department(const std::string& name)
        : name(name) {}

    void add(OrganizationComponent* component) {
        children.push_back(component);
    }

    void remove(OrganizationComponent* component) {
        // Remove component from children
    }

    void displayInfo() const override {
        std::cout << "Department: " << name << std::endl;
        for (const auto& child : children) {
            child->displayInfo();
        }
    }
};

int main() {
    // Creating employees
    OrganizationComponent* employee1 = new Employee("John Doe", "Software Engineer");
    OrganizationComponent* employee2 = new Employee("Jane Smith", "Product Manager");

    // Creating departments
    Department* development = new Department("Development");
    Department* marketing = new Department("Marketing");

    // Adding employees to departments
    development->add(employee1);
    marketing->add(employee2);

    // Adding sub-department to department
    Department* frontend = new Department("Frontend");
    development->add(frontend);

    // Displaying organization hierarchy
    development->displayInfo();
    marketing->displayInfo();

    // Cleaning up
    delete employee1;
    delete employee2;
    delete frontend;
    delete development;
    delete marketing;

    return 0;
}

 

해당 예시에서 베이스 클래스로 OrganizationComponent가 있습니다. 해당 클래스는 leaf 객체(Employee)와 composite 객체(Department) 모두에게 인터페이스를 제공합니다. 여기서 Employee 클래스는 개별 직원을 나타내고, Department 클래스는 직원과 하위 부서를 모두 포함하는 부서를 나타냅니다.

 

3. 장점

Hierarchical Structure: Composite 패턴은 객체를 트리 형태의 계층 구조로 구성함으로써 유연하고 중첩된 객체 구조를 만들 수 있습니다.

Uniform Treatment: Composite 패턴은 개별 객체와 객체 그룹을 일관되게 제어할 수 있도록 하기 때문에 객체 계층 구조의 구성 요소에 대한 일관적인 인터페이스를 제공합니다.

Simplified Client Code: 클라이언트는 개별 객체나 컴포지트 객체인지 상관없이 단일 인터페이스를 사용하여 계측 구조에 접근하여 상호작용 할 수 있습니다.

Dynamic Structure Modification: Composite 패턴은 실행 중에 객체 계층 구조에 구성 요소를 동적으로 추가하거나 제거할 수 있어 유연성을 높여줍니다.

 

4. 단점

Increased Complexity: Composite 패턴을 구현하면 복잡한 계층 구조를 다룰 때 코드 복잡도가 높아질 수 있습니다.

Performance Overhead: Composite 패턴은 재귀적인 성격을 가지고 있기 때문에 대규모 객체 구조에서 탐색과 같은 작업을 수행할 때 성능 부담이 커질 수 있습니다.

Limited Component Control: 일관적인 인터페이스를 통해 작업을 수행할 수 있다는 것은 개별 객체에 특정한 작업을 적용하기 어렵게 만들 수도 있습니다.

 

5. 결론

Composite 패턴을 사용하면 유연하고 일관성 있는 객체 계층 구조를 설계할 수 있고, 해당 구조와 원활한 상호 작용을 할 수 있습니다. 개별 객체와 객체 그룹을 일관되게 처리함으로써 클라이언트 코드를 간소화하고, 코드의 확장성과 유지보수성을 높여줍니다. 그러나 특정 시나리오에서 Composite 패턴을 사용하는 경우 복잡성과 성능에 미치는 영향을 신중히 고려해야 합니다.

반응형