- Structural Pattern.
#include <iostream>
#include <memory>
#include <vector>
#include <string>
using namespace std;
class File {
private:
string name;
size_t size;
public:
File(const string& name, size_t size) : name(name), size(size) {}
~File() {}
size_t getSize() const { return size; }
};
class Folder {
private:
string name;
vector<shared_ptr<File>> _fileList;
public:
Folder(const string& name) : name(name) {}
~Folder() {}
void add_item(shared_ptr<File> new_item) {
_fileList.push_back(new_item);
}
size_t getSize() const {
size_t result{ 0 };
for (const auto& file : _fileList) {
result += file->getSize();
}
return result;
}
};
int main() {
shared_ptr<Folder> MyFolder = make_shared<Folder>("folder");
MyFolder->add_item(make_shared<File>("a.txt", 10));
MyFolder->add_item(make_shared<File>("b.txt", 10));
MyFolder->add_item(make_shared<File>("c.txt", 10));
cout << MyFolder->getSize() << endl;
}
이런 식으로 될 것이다.
file은 이름과 사이즈를 가지고, getSize를 통해 본인의 크기를 return한다.
folder는 이름과 file vector를 가지고, getSize를 통해 본인이 가지고 있는 파일 전체의 크기를 return한다.
하지만 이렇게 만든다면 폴더 안에 파일만 들어갈 수 있게 되어, 우리가 흔히 아는 폴더/ 파일 구조와 다르게 된다.
폴더 안에 폴더가 들어가고, 그 안에 또 폴더와 파일이 들어가고... 이런 식으로도 가능해야 한다.
-> file/ folder의 상위 클래스읜 ITEM Class를 만들어 상속받고, folder에서는 ITEM Vector를 가지게 해 보자.
class ITEM{
protected:
string name;
public:
ITEM(const string &name) : name(name){}
virtual ~ITEM(){}
virtual void add_item(shared_ptr<ITEM> item) = 0;
virtual size_t getSize() const = 0;
};
ITEM 은 이렇게 공통된 부분을 가질 것이고,
class Folder : public ITEM {
private:
vector<shared_ptr<ITEM>> _fileList;
public:
Folder(const string& name) : ITEM(name) {}
~Folder() {}
void add_item(shared_ptr<ITEM> new_item) override {
_fileList.push_back(new_item);
}
size_t getSize() const override {
size_t result{ 0 };
for (auto& file : _fileList) {
result += file->getSize();
}
return result;
}
};
Folder는 이런 식으로 ITEM vector를 가지고, add_item을 통해 File이 아닌 ITEM 을 받아 벡터에 추가하게 된다.
class InvalidCmdException{};
class File : public ITEM {
private:
size_t size;
public:
File(const string& name, size_t size) : ITEM(name), size(size) {}
~File() {}
void add_item(shared_ptr<ITEM> item) override { throw InvalidCmdException();}
size_t getSize() const override { return size; }
};
File은 이런 식으로 getSize를 통해 본인의 사이즈를 내뿜게 되고,
add_item은 사용되지 않아야 함으로 File에서 해당 메소드가 불리면 throw를 통해 던져주었다.
그러면 main에서 사용할 때엔
int main() {
shared_ptr<Folder> MyFolder = make_shared<Folder>("folder");
shared_ptr<Folder> SubFolder = make_shared<Folder>("sub");
SubFolder->add_item(make_shared<File>("suba.txt", 1));
SubFolder->add_item(make_shared<File>("subb.txt", 2));
SubFolder->add_item(make_shared<File>("subc.txt", 3));
MyFolder->add_item(SubFolder);
MyFolder->add_item(make_shared<File>("a.txt", 10));
MyFolder->add_item(make_shared<File>("b.txt", 33));
MyFolder->add_item(make_shared<File>("c.txt", 25));
cout << MyFolder->getSize() << endl;
}
이런 식으로 folder를 만들고, 그 안에 폴더를 만들고, 그 안에 파일을 추가할 수 있게 된다.
이것을 class diagram으로 간단하게 나타내 보면
이와 같다.
재귀적 포함.
즉, A는 B를 포함할 수 있는데, A 자신도 포함할 수 있는 상태.
A와 B에 대해 공통의 base class를 만들어 해결.
재귀적 포함을 사용하여 복합 객체를 만드려고 하고, 본인도 넣고 본인 이외의 것도 넣을 수 있게 한다.
이 패턴을 composite 패턴이라고 한다.
* 잘못된 정보는 알려주시면 감사하겠습니다.
'Design Pattern' 카테고리의 다른 글
iterator pattern (0) | 2022.11.20 |
---|---|
Template Method (0) | 2022.09.06 |
Observer Pattern (0) | 2022.08.14 |
Decorator pattern (0) | 2022.07.26 |
factory method (0) | 2022.06.07 |