Architecture & Design Pattern 전체 링크
참고
- 반복자, 이터레이터 패턴 (Iterator Pattern)
복합체, 컴포지트 패턴 (Composite Pattern) - 구조 패턴
- 객체들을 트리 구조로 구성하여 부분-전체 계층 구조(part-whole hierarchy)를 표현하는 패턴
- 클라이언트에서 개별 객체와 다른 객체들로 구성된 복합 객체(composite)를 똑같은 방식으로 다룰 수 있다.
구현
- Component (구성 요소) : 인터페이스 또는 추상 클래스를 정의, 복합 객체와 개별 객체에 공통된 동작을 정의
- Leaf (원소) : Component를 구현, 단일 객체, 메서드를 구현
- Composite (복합체) : Component를 구현, Leaf 객체나 다른 Composite 객체들의 집합, 자식 요소 관리
- 클라이언트는 Component 인터페이스를 사용하여 Leaf나 Composite 객체들을 동일하게 다룬다.
장점
- OCP(개방-폐쇄 원칙), 코드를 수정하지 않고 새로운 종류의 개별 객체나 복합 객체를 추가하기 쉬움 (확장성)
- 다형성과 재귀 함수를 이용해 개별 객체와 복합 객체를 동일한 방식으로 처리 (유연성)
- 객체들의 계층 구조를 나타내는 데 유용, 복잡한 구조를 간결히 표현 (구조적 일관성)
단점
- 계층 구조가 복잡할수록 이해하고 유지보수가 어려울 수 있다.
- 너무 간단한 구조는 트리 구조인 복합체 패턴을 적용하는데 비용이 크다.
- 기능이 서로 다른 클래스들은 공통 인터페이스를 만들기 어렵기 때문에 컴포넌트 인터페이스를 일반화하기 어렵다.
트리 형태의 건물
이터레이터 패턴으로 건물을 관리했던 건물주를 조금 더 확장해 보자.
→ 각 건물에는 연관된 또 다른 작은 건물이 있다고 가정하자. (subApart, subSubApart, subOffice ....)
그리고 이런 계층 구조를 관리하기 위해 컴포지트 패턴을 사용해 보자.
클라이언트는 Component 인터페이스를 이용해서 Leaf나 Composite 객체를 다룰 수 있다.
Component는 Leaf와 Composite 모두 적용되는 인터페이스나 추상 클래스가 된다.
BuildingComposite는 어떤 타입인지 (아파트, 오피스, 상가 등) 알 수 있는 정보가 있고,
BuildingComponent(← Leaf)를 더하거나 제거하고, 모든 Leaf를 출력하는 print를 구현한다.
class BuildingComposite : public BuildingComponent
{
public:
BuildingComposite() = default;
BuildingComposite(string type) : buildingType(type) {}
void add(BuildingComponent* bc) override { buildingComponent.push_back(bc); }
void remove(BuildingComponent* bc) override
{
if (!buildingComponent.empty())
{
delete bc;
buildingComponent.remove(bc);
}
}
void print() const override
{
cout << "-----------------------------------------" << endl;
cout << "Type : " << getType() << endl;
for (const auto &bc : buildingComponent) bc->print();
cout << "-----------------------------------------" << endl;
}
string getType() const override { return buildingType; }
private:
string buildingType;
list<BuildingComponent*> buildingComponent;
};
Leaf는 누가 몇 층에 사는지에 대한 정보와 Leaf를 출력하는 print를 구현한다.
즉, 복합 객체의 원소에 해당하는 정보와 메서드를 구현한다.
class BuildingLeaf : public BuildingComponent
{
public:
BuildingLeaf() = default;
BuildingLeaf(int f, string n) : floor(f), name(n) {}
void print() const override
{ cout << "(" << getFloor() << ") [ " << getName() << " ]" << endl; }
int getFloor() const override { return floor; }
string getName() const override { return name; }
private:
int floor;
string name;
list<BuildingComponent*> buildingComponent;
};
BuildingComponent는 BuildingComposite, BuildingLeaf에 적용되는 모든 메서드를 가진다.
Leaf나 Composite에 구현할 필요가 없는 메서드가 서로 존재하기 때문에 아래와 같이 exception으로 구현하였다.
class BuildingComponent
{
public:
virtual ~BuildingComponent() = default;
virtual void add(BuildingComponent* buildingComponent)
{ throw invalid_argument("add(): Unsupported Operation"); }
virtual void remove(BuildingComponent* buildingComponent)
{ throw invalid_argument("remove(): Unsupported Operation"); }
// virtual BuildingComponent* getChild(int i) const
// { throw invalid_argument("getChid(): Unsupported Operation"); }
virtual int getFloor() const { throw invalid_argument("getFloor(): Unsupported Operation"); }
virtual string getName() const { throw invalid_argument("getName(): Unsupported Operation"); }
virtual string getType() const { throw invalid_argument("getType(): Unsupported Operation"); }
virtual void print() const { throw invalid_argument("print(): Unsupported Operation"); }
};
건물주(클라이언트)는 최상의 건물 구성요소만 가지면 된다.
class Manager
{
public:
Manager(BuildingComponent* bc) : allBuildings(move(bc)) { }
void printBuilding() { allBuildings->print(); }
private:
BuildingComponent* allBuildings;
};
전체 코드는 다음과 같다.
#include <iostream>
#include <string>
#include <vector>
#include <list>
using namespace std;
class BuildingComponent
{
public:
virtual ~BuildingComponent() = default;
virtual void add(BuildingComponent* buildingComponent)
{ throw invalid_argument("add(): Unsupported Operation"); }
virtual void remove(BuildingComponent* buildingComponent)
{ throw invalid_argument("remove(): Unsupported Operation"); }
// virtual BuildingComponent* getChild(int i) const
// { throw invalid_argument("getChid(): Unsupported Operation"); }
virtual int getFloor() const { throw invalid_argument("getFloor():Unsupported Operation"); }
virtual string getName() const { throw invalid_argument("getName():Unsupported Operation"); }
virtual string getType() const { throw invalid_argument("getType():Unsupported Operation"); }
virtual void print() const { throw invalid_argument("print():Unsupported Operation"); }
};
class BuildingComposite : public BuildingComponent
{
public:
BuildingComposite() = default;
BuildingComposite(string type) : buildingType(type) {}
void add(BuildingComponent* bc) override { buildingComponent.push_back(bc); }
void remove(BuildingComponent* bc) override
{
if (!buildingComponent.empty())
{
delete bc;
buildingComponent.remove(bc);
}
}
void print() const override
{
cout << "-----------------------------------------" << endl;
cout << "Type : " << getType() << endl;
for (const auto &bc : buildingComponent) bc->print();
cout << "-----------------------------------------" << endl;
}
string getType() const override { return buildingType; }
private:
string buildingType;
list<BuildingComponent*> buildingComponent;
};
class BuildingLeaf : public BuildingComponent
{
public:
BuildingLeaf() = default;
BuildingLeaf(int f, string n) : floor(f), name(n) {}
void print() const override
{ cout << "(" << getFloor() << ") [ " << getName() << " ]" << endl; }
int getFloor() const override { return floor; }
string getName() const override { return name; }
private:
int floor;
string name;
list<BuildingComponent*> buildingComponent;
};
/* ----------------- Manager ------------------ */
class Manager
{
public:
Manager(BuildingComponent* bc) : allBuildings(move(bc)) { }
void printBuilding() { allBuildings->print(); }
private:
BuildingComponent* allBuildings;
};
int main(void)
{
BuildingComposite* apart = new BuildingComposite("Apart");
BuildingComposite* office = new BuildingComposite("Office");
BuildingComposite* store = new BuildingComposite("Store");
BuildingComposite* subApart = new BuildingComposite("Sub Apart");
BuildingComposite* subOffice = new BuildingComposite("Sub Office");
BuildingComposite* subSubApart1 = new BuildingComposite("Sub Sub Apart 1");
BuildingComposite* subSubApart2 = new BuildingComposite("Sub Sub Apart 2");
BuildingComposite* allBuildings = new BuildingComposite("All Building");
allBuildings->add(apart);
allBuildings->add(office);
allBuildings->add(store);
// ------------------------------------------------------------------ //
vector<BuildingLeaf*> apartItems{
new BuildingLeaf(1, "blood"),
new BuildingLeaf(5, "straw"),
new BuildingLeaf(3, "berry"),
};
for (auto &item : apartItems) apart->add(item);
vector<BuildingLeaf*> subApartItems{
new BuildingLeaf(10, "sub apart 1"),
new BuildingLeaf(11, "sub apart 2"),
new BuildingLeaf(13, "sub apart 3"),
};
for (auto &item : subApartItems) subApart->add(item);
apart->add(subApart);
vector<BuildingLeaf*> subSubApartItems1{
new BuildingLeaf(131, "sub sub apart 1"),
new BuildingLeaf(185, "sub sub apart 2"),
new BuildingLeaf(172, "sub sub apart 3"),
};
vector<BuildingLeaf*> subSubApartItems2{
new BuildingLeaf(231, "sub sub apart 4"),
new BuildingLeaf(285, "sub sub apart 5"),
};
for (auto &item : subSubApartItems1) subSubApart1->add(item);
for (auto &item : subSubApartItems2) subSubApart2->add(item);
subApart->add(subSubApart1);
subApart->add(subSubApart2);
// ------------------------------------------------------------------ //
vector<BuildingLeaf*> officeItems{
new BuildingLeaf(1, "LG"),
new BuildingLeaf(5, "NC"),
new BuildingLeaf(4, "SK"),
new BuildingLeaf(3, "HYUNDAI"),
};
for (auto &item : officeItems) office->add(item);
vector<BuildingLeaf*> subOfficeItems{
new BuildingLeaf(30, "sub office 1"),
new BuildingLeaf(31, "sub office 2"),
new BuildingLeaf(32, "sub office 3"),
};
for (auto &item : subOfficeItems) subOffice->add(item);
office->add(subOffice);
// ------------------------------------------------------------------ //
vector<BuildingLeaf*> storeItems{
new BuildingLeaf(7, "steak"),
new BuildingLeaf(3, "chicken"),
new BuildingLeaf(1, "fork"),
};
for (auto &item : storeItems) store->add(item);
// ------------------------------------------------------------------ //
Manager* manager = new Manager(allBuildings);
manager->printBuilding();
for (auto &item : apartItems) delete item;
for (auto &item : subApartItems) delete item;
for (auto &item : subSubApartItems1) delete item;
for (auto &item : subSubApartItems2) delete item;
for (auto &item : officeItems) delete item;
for (auto &item : subOfficeItems) delete item;
for (auto &item : storeItems) delete item;
return 0;
}
현재 Component 클래스는 Composite와 Leaf의 기능이 모두 들어가 있어서 안전성이 떨어진다.
클라이언트가 Composite이나 Leaf가 사용해서는 안 될 메서드를 사용하려고 할 수 있기 때문이다.
이터레이터 패턴 적용
이제 컴포지트 패턴 내에서 이터레이터 패턴을 적용해 보자.
template <typename T>
class Iterator
{
public:
virtual ~Iterator() = default;
virtual T* next() = 0;
virtual bool hasNext() = 0;
virtual void remove() = 0;
};
Component에는 createIterator 메서드를 추가한다.
class BuildingComponent
{
public:
virtual ~BuildingComponent() = default;
virtual Iterator<BuildingComponent>* createIterator() = 0;
...
};
이제 Composite와 Leaf에도 createIterator를 구현해야 한다.
먼저 Leaf에 대해 반복 작업을 하는 CompositeIterator를 만들자.
생성자에는 반복 작업을 할 최상위 객체가 전달된다.
그리고 iterator의 next 메서드를 재귀적으로 호출해서 모든 원소에 접근한다.
class CompositeIterator : public Iterator<BuildingComponent>
{
public:
CompositeIterator(Iterator<BuildingComponent>* iterator) { stk.push(iterator); }
BuildingComponent* next() override
{
if (!hasNext()) return nullptr;
Iterator<BuildingComponent>* iterator = stk.top();
BuildingComponent* component = iterator->next(); // recursive
stk.push(component->createIterator());
return component;
}
bool hasNext() override
{
if (stk.empty()) return false;
Iterator<BuildingComponent>* iterator = stk.top();
if (!iterator->hasNext())
{
stk.pop();
return hasNext(); // recursive
}
return true;
}
void remove() override
{ throw invalid_argument("CompositeIterator::remove(): Unsupported Operation"); }
private:
stack<Iterator<BuildingComponent>*> stk;
};
BuildingIterator는 다음과 같다.
class BuildingIterator : public Iterator<BuildingComponent>
{
public:
BuildingIterator(list<BuildingComponent*> &bc) : components(bc), iter(components.begin()) {}
BuildingComponent* next() override { return *iter++; }
bool hasNext() override { return iter != components.end(); }
void remove() override
{ throw invalid_argument("BuildingIterator::remove(): Unsupported Operation"); }
private:
list<BuildingComponent*> &components;
list<BuildingComponent*>::iterator iter;
};
BuildingComponent로 BuildingIterator를 만들고 CompositeIterator를 생성한다.
class BuildingComposite : public BuildingComponent
{
public:
BuildingComposite() = default;
BuildingComposite(string type) : buildingType(type) {}
Iterator<BuildingComponent>* createIterator() override
{
if (iterator == nullptr)
{
buildingIterator = make_unique<BuildingIterator>(buildingComponent);
iterator = make_unique<CompositeIterator>(buildingIterator.get());
}
return iterator.get();
}
...
private:
string buildingType;
list<BuildingComponent*> buildingComponent;
unique_ptr<BuildingIterator> buildingIterator = nullptr;
unique_ptr<Iterator<BuildingComponent>> iterator = nullptr;
};
BuildingLeaf는 반복 작업을 할 대상이 없으므로, createIterator 메서드를 구현하기 애매해진다.
그냥 null을 return해도 되지만, 해당 메서드를 호출하는 클라이언트가 null check를 할 필요가 있다.
따라서 hasNext()가 호출될 때, false를 return하는 널 이터레이터를 선언해서 아무 일도 하지 않도록 처리한다.
class NullIterator : public Iterator<BuildingComponent>
{
public:
BuildingComponent* next() override { return nullptr; }
bool hasNext() override { return false; }
void remove() override
{ throw invalid_argument("NullIterator::remove(): Unsupported Operation"); }
};
class BuildingLeaf : public BuildingComponent
{
public:
BuildingLeaf() = default;
BuildingLeaf(int f, string n) : floor(f), name(n) {}
Iterator<BuildingComponent>* createIterator() override { return nullIterator.get(); }
private:
...
static unique_ptr<NullIterator> nullIterator;
};
unique_ptr<NullIterator> BuildingLeaf::nullIterator = make_unique<NullIterator>();
이제 Iterator를 이용하여 홀수 층 건물만 출력하도록 해보자.
getFloor()가 구현되지 않은 BuildingComposite는 catch에 의해 아무 일도 하지 않는다.
class Manager
{
public:
Manager(BuildingComponent* bc) : allBuildings(bc) { }
void printBuilding() { allBuildings->print(); }
void printBuildingIter()
{
Iterator<BuildingComponent>* iterator = allBuildings->createIterator();
cout << "Print with Iterator" << endl;
while (iterator->hasNext())
{
BuildingComponent* bc = iterator->next();
try
{
if (bc->getFloor() % 2 == 1)
bc->print();
}
catch (invalid_argument e) {}
}
}
private:
BuildingComponent* allBuildings;
};
홀수 층만 출력되는 것을 확인해 보자.
전체 코드는 다음과 같다.
#include <iostream>
#include <string>
#include <memory>
#include <vector>
#include <list>
#include <stack>
using namespace std;
template <typename T>
class Iterator
{
public:
virtual ~Iterator() = default;
virtual T* next() = 0;
virtual bool hasNext() = 0;
virtual void remove() = 0;
};
class BuildingComponent
{
public:
virtual ~BuildingComponent() = default;
virtual Iterator<BuildingComponent>* createIterator() = 0;
virtual void add(BuildingComponent* buildingComponent)
{ throw invalid_argument("add(): Unsupported Operation"); }
virtual void remove(BuildingComponent* buildingComponent)
{ throw invalid_argument("remove(): Unsupported Operation"); }
// virtual BuildingComponent* getChild(int i) const
// { throw invalid_argument("getChid(): Unsupported Operation"); }
virtual int getFloor() const { throw invalid_argument("getFloor():Unsupported Operation"); }
virtual string getName() const { throw invalid_argument("getName():Unsupported Operation"); }
virtual string getType() const { throw invalid_argument("getType():Unsupported Operation"); }
virtual void print() const { throw invalid_argument("print():Unsupported Operation"); }
};
class CompositeIterator : public Iterator<BuildingComponent>
{
public:
CompositeIterator(Iterator<BuildingComponent>* iterator) { stk.push(iterator); }
BuildingComponent* next() override
{
if (!hasNext()) return nullptr;
Iterator<BuildingComponent>* iterator = stk.top();
BuildingComponent* component = iterator->next(); // recursive
stk.push(component->createIterator());
return component;
}
bool hasNext() override
{
if (stk.empty()) return false;
Iterator<BuildingComponent>* iterator = stk.top();
if (!iterator->hasNext())
{
stk.pop();
return hasNext(); // recursive
}
return true;
}
void remove() override
{ throw invalid_argument("CompositeIterator::remove(): Unsupported Operation"); }
private:
stack<Iterator<BuildingComponent>*> stk;
};
class BuildingIterator : public Iterator<BuildingComponent>
{
public:
BuildingIterator(list<BuildingComponent*> &bc) : components(bc), iter(components.begin()) {}
BuildingComponent* next() override { return *iter++; }
bool hasNext() override { return iter != components.end(); }
void remove() override
{ throw invalid_argument("BuildingIterator::remove(): Unsupported Operation"); }
private:
list<BuildingComponent*> &components;
list<BuildingComponent*>::iterator iter;
};
class NullIterator : public Iterator<BuildingComponent>
{
public:
BuildingComponent* next() override { return nullptr; }
bool hasNext() override { return false; }
void remove() override
{ throw invalid_argument("NullIterator::remove(): Unsupported Operation"); }
};
class BuildingComposite : public BuildingComponent
{
public:
BuildingComposite() = default;
BuildingComposite(string type) : buildingType(type) {}
Iterator<BuildingComponent>* createIterator() override
{
if (iterator == nullptr)
{
buildingIterator = make_unique<BuildingIterator>(buildingComponent);
iterator = make_unique<CompositeIterator>(buildingIterator.get());
}
return iterator.get();
}
void add(BuildingComponent* bc) override { buildingComponent.push_back(bc); }
void remove(BuildingComponent* bc) override { buildingComponent.remove(bc); }
void print() const override
{
cout << "-----------------------------------------" << endl;
cout << "Type : " << getType() << endl;
for (const auto &bc : buildingComponent) bc->print();
cout << "-----------------------------------------" << endl;
}
string getType() const override { return buildingType; }
private:
string buildingType;
list<BuildingComponent*> buildingComponent;
unique_ptr<BuildingIterator> buildingIterator = nullptr;
unique_ptr<Iterator<BuildingComponent>> iterator = nullptr;
};
class BuildingLeaf : public BuildingComponent
{
public:
BuildingLeaf() = default;
BuildingLeaf(int f, string n) : floor(f), name(n) {}
Iterator<BuildingComponent>* createIterator() override { return nullIterator.get(); }
void print() const override
{ cout << "(" << getFloor() << ") [ " << getName() << " ]" << endl; }
int getFloor() const override { return floor; }
string getName() const override { return name; }
private:
int floor;
string name;
list<BuildingComponent*> buildingComponent;
static unique_ptr<NullIterator> nullIterator;
};
unique_ptr<NullIterator> BuildingLeaf::nullIterator = make_unique<NullIterator>();
/* ----------------- Manager ------------------ */
class Manager
{
public:
Manager(BuildingComponent* bc) : allBuildings(bc) { }
void printBuilding() { allBuildings->print(); }
void printBuildingIter()
{
Iterator<BuildingComponent>* iterator = allBuildings->createIterator();
cout << "Print with Iterator" << endl;
while (iterator->hasNext())
{
BuildingComponent* bc = iterator->next();
try
{
if (bc->getFloor() % 2 == 1)
bc->print();
}
catch (invalid_argument e) {}
}
}
private:
BuildingComponent* allBuildings;
};
int main(void)
{
BuildingComposite* apart = new BuildingComposite("Apart");
BuildingComposite* office = new BuildingComposite("Office");
BuildingComposite* store = new BuildingComposite("Store");
BuildingComposite* subApart = new BuildingComposite("Sub Apart");
BuildingComposite* subOffice = new BuildingComposite("Sub Office");
BuildingComposite* subSubApart1 = new BuildingComposite("Sub Sub Apart 1");
BuildingComposite* subSubApart2 = new BuildingComposite("Sub Sub Apart 2");
BuildingComposite* allBuildings = new BuildingComposite("All Building");
allBuildings->add(apart);
allBuildings->add(office);
allBuildings->add(store);
// ------------------------------------------------------------------ //
vector<BuildingLeaf*> apartItems{
new BuildingLeaf(1, "blood"),
new BuildingLeaf(5, "straw"),
new BuildingLeaf(3, "berry"),
};
for (auto &item : apartItems) apart->add(item);
vector<BuildingLeaf*> subApartItems{
new BuildingLeaf(10, "sub apart 1"),
new BuildingLeaf(11, "sub apart 2"),
new BuildingLeaf(13, "sub apart 3"),
};
for (auto &item : subApartItems) subApart->add(item);
apart->add(subApart);
vector<BuildingLeaf*> subSubApartItems1{
new BuildingLeaf(131, "sub sub apart 1"),
new BuildingLeaf(185, "sub sub apart 2"),
new BuildingLeaf(172, "sub sub apart 3"),
};
vector<BuildingLeaf*> subSubApartItems2{
new BuildingLeaf(231, "sub sub apart 4"),
new BuildingLeaf(285, "sub sub apart 5"),
};
for (auto &item : subSubApartItems1) subSubApart1->add(item);
for (auto &item : subSubApartItems2) subSubApart2->add(item);
subApart->add(subSubApart1);
subApart->add(subSubApart2);
// ------------------------------------------------------------------ //
vector<BuildingLeaf*> officeItems{
new BuildingLeaf(1, "LG"),
new BuildingLeaf(5, "NC"),
new BuildingLeaf(4, "SK"),
new BuildingLeaf(3, "HYUNDAI"),
};
for (auto &item : officeItems) office->add(item);
vector<BuildingLeaf*> subOfficeItems{
new BuildingLeaf(30, "sub office 1"),
new BuildingLeaf(31, "sub office 2"),
new BuildingLeaf(32, "sub office 3"),
};
for (auto &item : subOfficeItems) subOffice->add(item);
office->add(subOffice);
// ------------------------------------------------------------------ //
vector<BuildingLeaf*> storeItems{
new BuildingLeaf(7, "steak"),
new BuildingLeaf(3, "chicken"),
new BuildingLeaf(1, "fork"),
};
for (auto &item : storeItems) store->add(item);
// ------------------------------------------------------------------ //
Manager* manager = new Manager(allBuildings);
manager->printBuildingIter();
for (auto &item : apartItems) delete item;
for (auto &item : subApartItems) delete item;
for (auto &item : subSubApartItems1) delete item;
for (auto &item : subSubApartItems2) delete item;
for (auto &item : officeItems) delete item;
for (auto &item : subOfficeItems) delete item;
for (auto &item : storeItems) delete item;
return 0;
}
'개발 > Architecture & Design Pattern' 카테고리의 다른 글
C++ - 의존 역전 원칙 (DIP, Dependency Inversion Principle) (0) | 2024.02.12 |
---|---|
C++ - 데코레이터 패턴 (Decorator Pattern) (1) | 2024.02.10 |
C++ - 반복자, 이터레이터 패턴 (Iterator Pattern) (0) | 2024.02.04 |
C++ - 옵저버 패턴 (Observer Pattern) (1) | 2024.01.30 |
C++ - 전략, 스트래티지 패턴 (Strategy Pattern) (0) | 2024.01.29 |
댓글