컴포지트 패턴이란?
- 부분과 전체 객체를 동일하게 처리하는 것이 목표
- 즉, 복합 객체(Composite)와 단일 객체(Leaf)를 동일한 방식으로 다룰 수 있도록 하여, 트리 구조를 구현하거나 부분-전체 관계 처리하는데 유용
구성 요소
1. Component (컴포넌트)
공용 인터페이스를 정의하여, Leaf와 Composite가 공유하는 기본 메서드를 선언
이 메서드는 일반적으로 operation()이나 showDetails()와 같은 형태로 구현
2. Leaf (리프)
단일 객체로, 더 이상 자식 객체를 가지지 않음.
실제 작업을 수행하는 객체로, 컴포지트 객체에 의해 포함되지 않는 개별 객체
3. Composite (컴포지트)
복합 객체로, 여러 자식 객체를 포함할 수 있음.
자식 객체들에 대한 작업을 처리하거나, 자식들에게 작업을 위임하는 역할 수행
4. Client (클라이언트)
Component 인터페이스를 통해 리프 객체와 컴포지트 객체를 동일하게 처리하는 코드
클라이언트는 자식 객체와 부모 객체를 구별하지 않고 공통의 메서드를 호출하여 작업 수행
패턴 흐름
1. Component : operation() 메서드 정의. Leaf와 Composite는 이 메서드를 공유하여 구현
2. Leaf : Component 인터페이스를 구현하여, operation()을 실제로 수행하는 단일 객체. 자식 없이 자신만의 기능 처리
3. Composite : Component 인터페이스를 구현하고, 여러 자식 객체들을 관리하는 역할.
자식 객체들로 리프 객체나 다른 컴포지트 객체 포함할 수 있음.
Operation()을 호출하면, 자신이 포함하는 자식 객체들에게 작업을 위임하거나, 자신의 작업을 수행
4. Client : Component 인터페이스를 사용하여, 단일 객체와 복합 객체를 동일하게 다루며 작업 수행
컴포지트 패턴 예제 (C++)
1. #include <iostream>
2. #include <list>
3.
4. using namespace std;
5.
6. class Unit{
7. public:
8. virtual void SelectUnit() = 0;
9. virtual void Add(Unit* pUnit) {}
10. virtual void Removd(Unit* pUnit) {}
11. virtual Unit* GetChild(int nIndex) { return NULL; }
12. };
13.
14. class DemonHunter : public Unit{
15. public:
16. void SelectUnit(){
17. cout << "Select Demon Hunter" << endl;
18. }
19. };
20.
21. class Archer : public Unit{
22. public:
23. void SelectUnit(){
24. cout << "Select Archer" << endl;
25. }
26. };
27.
28.
29. class Hunter : public Unit{
30. public:
31. void SelectUnit(){
32. cout << "Select Hunter" << endl;
33. }
34. };
35.
36. class GroupUnit : public Unit{
37. public:
38. GroupUnit(int number) : groupNumber(number) {}
39. void SelectUnit(){
40. cout << groupNumber << " 번 그룹 선택" << endl;
41.
42. for(list<Unit*>::iterator iter = m_listUnit.begin(); iter != m_listUnit.end(); ++iter){
43. (*iter)->SelectUnit();
44. }
45. }
46.
47. void Add(Unit* pItem){
48. m_listUnit.push_front(pItem);
49. }
50.
51. void Remove(Unit* pItem){
52. for(list<Unit*>::iterator iter = m_listUnit.begin(); iter != m_listUnit.end(); ++iter){
53. if(*iter == pItem){
54. m_listUnit.erase(iter);
55. return;
56. }
57. }
58. }
59.
60. Unit* GetChild(int nIndex){
61. if(!m_listUnit.empty() && nIndex >= 0){
62. int nItemIndex;
63. list<Unit*>::iterator iter;
64.
65. for(nItemIndex = 0, iter = m_listUnit.begin(); iter != m_listUnit.end(); ++iter, ++nItemIndex){
66. if(nItemIndex == nIndex)
67. return (*iter);
68. }
69. }
70. return NULL;
71. }
72.
73. protected:
74. list<Unit*> m_listUnit;
75. int groupNumber;
76. };
77.
78.
79.
80. int main() {
81. Archer* pArcher = new Archer;
82. Hunter* pHunter = new Hunter;
83. DemonHunter* pDemonHunter = new DemonHunter;
84.
85. cout << "단일 유닛 선택" << endl;
86. pDemonHunter->SelectUnit();
87.
88. cout << "1번 그룹 지정" << endl;
89. GroupUnit* pGroup = new GroupUnit(1);
90.
91. pGroup->Add(pArcher);
92. pGroup->Add(pHunter);
93. pGroup->SelectUnit();
94.
95.
96. cout << "2번 그룹 지정" << endl;
97.
98. GroupUnit* pGiantGroup = new GroupUnit(2);
99.
100. pGiantGroup->Add(pDemonHunter);
101. pGiantGroup->Add(pGroup);
102.
103. pGiantGroup->SelectUnit();
104.
105. delete pGroup;
106. delete pArcher;
107. delete pHunter;
108. delete pDemonHunter;
109.
110. return EXIT_SUCCESS;
111. }
112.
컴포지트 패턴 장점
1. Leaf와 Composite 객체를 동일한 방식으로 다룰 수 있기 때문에, 클라이언트 코드가 간단하고 일관
2. 트리 구조로 객체를 추가/제거하는데 용이 -> 확장성이 뛰어남
3. 트리 구조의 요소를 동적으로 변경할 수 있기 때문에, 객체의 계층 구조를 실시간으로 변경하거나 확장 가능
4. 부분-전체 관계를 명확하게 표현할 수 있음
컴포지트 패턴 단점
1. 트리 구조를 관리하고 객체들이 서로 결합되는 방식에 대한 추상화가 추가되기 때문에, 설계 복잡
2. 트리 구조의 깊이가 깊어질수록 탐색이나 연산에 대한 성능에 영향
3. 객체들이 서로 의존하게 되어, 객체 간의 결합도가 높아질 수 있음. 이는 변경에 민감한 시스템이 됨
'Unity > 디자인 패턴' 카테고리의 다른 글
| 디자인 패턴 (구조 패턴) - 퍼사드 패턴 (Facade Pattern) (0) | 2025.08.30 |
|---|---|
| 디자인 패턴 (구조 패턴) - 데코레이터 패턴 (Decorator Pattern) (0) | 2025.08.30 |
| 디자인 패턴 (구조 패턴) - 어댑터 패턴 (Adapter Pattern) (0) | 2025.08.30 |
| 디자인 패턴 (생성 패턴) - 빌더 패턴 (Builder Pattern) (0) | 2025.08.30 |
| 디자인 패턴 (생성 패턴) - 추상 팩토리 패턴 (Abstract Factory Pattern) (0) | 2025.08.30 |