S E P H ' S
[구조 패턴] 컴포지트(Composite) 패턴 본문
컴포지트(Composite) 패턴
OOP에서 컴포지트는 하나 이상의 유사한 객체를 구성으로 설계된 객체로 모두 유사한 기능을 나타낸다.
이를 통해 객체 그룹을 조작하는 것처럼, 단일 객체를 조작할 수 있다.
컴포지트 패턴이란?
컴포지트 패턴은 클라이언트가 복합 객체(group of object)나 단일 객체를 동일하게 취급하는 것을 목적으로 한다.
여기서 컴포지트의 의도는 트리 구조로 작성하여, 전체-부분(whole-part)관계를 표현하는 것이다.
트리구조를 다룰때, 프로그래머는 리프노드와 브랜치를 구별해야 한다.
여기서 코드는 많은 복잡성을 만들어 많은 에러를 초래한다.
이를 해결하기 위해, 복잡하고 원시적인 객체를 동일하게 취급하기 위한 인터페이스를 작성할 수 있다.
이러한 컴포지트 패턴은 인터페이스와 본연의 컴포지트 개념을 활용한다.
언제 사용할까?
복합 객체와 단일 객체의 처리 방법이 다르지 않을 경우, 전체-부분 관계로 정의할 수 있다.
전체-부분 관계의 대표적 예시는 directory - file 이 존재한다.
이러한 전체-부분 관계를 효율적으로 정의할 때 유용하다.
- 전체-부분 관계를 트리 구조로 표현하고 싶을 경우
- 전체-부분 관계를 클라이언트에서 부분, 관계를 균일하게 처리하고 싶을 경우
구조
- Component : 컴포지션(구성 요소들)의 모든 개체에 대한 기본 인터페이스이다. Composite, Leaf을 관리하는 공통 메소드가 있는 인터페이스 또는 추상 클래스여야 한다.
- Leaf : 기본 구성 요소의 기본 동작을 구현한다. 다른 개체에 대한 참조를 포함하지 않는다.
- Composite : 리프 요소가 있다. 기본 구성 요소 메소드를 구현하고 자식 관련 작업을 정의한다.
- Client - 기본 구성 요소 개체를 사용하여 컴포지션 요소에 액세스 할 수 있다.
장단점
장점
- 객체들이 모두 같은 타입으로 취급되므로 새로운 클래스 추가가 용이하다.
- 단일 객체 및 집합 객체를 구분하지 않고 코드 작성이 가능하여 사용자 코드가 단순해진다.
- 단일 객체와 집합 객체로 구성된 하나의 일관된 클래스 계통을 정의한다. 런타임 단일 객체와 집합 객체를 구분 하지 않고 일관된 프로그래밍이 가능하다.
단점
- 설계가 지나치게 범용성을 많이 가진다. 기능이 크게 다른 클래스에 공통 인터페이스를 제공하는 것이 어렵다.
- 즉, 설계를 일반화 시켜 객체간의 구분, 제약이 힘들다.
예시
기본 구성 요소 (The Base Component)
먼저 구성 요초 개체로 부서 인터페이스를 정의했다.
public interface Department {
void printDepartmentName();
}
리프 (Leafs)
리프 요소에는 재무 및 영업 부서에 대한 클래스를 정의했다.
public class FinancialDepartment implements Department {
private Integer id;
private String name;
public void printDepartmentName() {
System.out.println(getClass().getSimpleName());
}
// standard constructor, getters, setters
}
public class SalesDepartment implements Department {
private Integer id;
private String name;
public void printDepartmentName() {
System.out.println(getClass().getSimpleName());
}
// standard constructor, getters, setters
}
주의 깊게 봐야 할 것은 두 클래스 모두 기본 구성 요소에서 printDepartmentName() 메소드를 구현하여 각각의 클래스 이름을 출력한다.
또한 이들은 리프 클래스이므로 다른 부서 개체를 포함하지 않는다.
컴포지트 (The Composite Element)
public class HeadDepartment implements Department {
private Integer id;
private String name;
private List<Department> childDepartments;
public HeadDepartment(Integer id, String name) {
this.id = id;
this.name = name;
this.childDepartments = new ArrayList<>();
}
public void printDepartmentName() {
childDepartments.forEach(Department::printDepartmentName);
}
public void addDepartment(Department department) {
childDepartments.add(department);
}
public void removeDepartment(Department department) {
childDepartments.remove(department);
}
}
HeadDepartment는 Department 구성 요소 컬렉션과 목록에서 요소를 추가하고 제거하는 메소드를 보유하는 복합 클래스이다.
HeadDepartment의 printDepartmentName에서는 모든 리프 요소 목록을 출력하도록하고 각각에 대한 적절한 메소드를 호출하여 구현된다.
테스트
public class CompositeDemo {
public static void main(String[] args) {
Department salesDepartment = new SalesDepartment(1, "Sales Department");
Department financialDepartment = new FinancialDepartment(2, "Financial Department");
HeadDepartment headDepartment = new HeadDepartment(3, "Head Department");
headDepartment.addDepartment(salesDepartment);
headDepartment.addDepartment(financialDepartment);
headDepartment.printDepartmentName();
}
}
재무 및 영업 부서에 대한 인스턴스를 생성하고 헤드 부서를 인스턴스화 한다. 그리고 헤드 부서에 영업 부서와 재무 부서를 추가시키고 출력을 하면 두 부서의 이름이 출력된다. 이렇게 구조 패턴 중 컴포지트 패턴에 대해 알아봤다. 설계 도중 각 요소들이 공통된 동작을 하고 이에 대해 동일하게 관리가 필요하다면 컴포지트 패턴을 떠올려 코드를 작성하면 되겠다는 생각을 하게 되었다.
'Programing & Coding > Design Pattern' 카테고리의 다른 글
[행동 패턴] 상태(State) 패턴 (0) | 2023.03.19 |
---|---|
[행동 패턴] 책임 연쇄(Chain of Responsibility) 패턴 (0) | 2023.03.19 |
[생성 패턴] 프로토타입(Prototype) 패턴 (0) | 2023.03.05 |
[생성 패턴] 빌더(Builder) 패턴 (0) | 2023.03.05 |
[생성 패턴] 추상 팩토리 (Abstract Factory) 패턴 (0) | 2023.03.05 |