Design Pattern

Composite Pattern

sseram 2022. 5. 18. 00:37
반응형

- Structural Pattern.

 

 

폴더 / 파일 구조를 생각하여 코드를 짜 보자.
 
- 폴더와 파일 class를 만들어야 한다.
- 각각은 name을 가진다.
- 폴더는 파일을 포함할 수 있다.
- 파일은 각 사이즈를 가지고, 폴더는 각각의 사이즈를 가지진 않지만 본인 내부에 있는 파일 사이즈의 합을 내뿜는다.

해당 구조를 생각하여 간단하게 코드를 작성해 본다면
 
 
#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를 통해 던져주었다.

File에서 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