diff --git a/CMakeLists.txt b/CMakeLists.txt index 4b60f5e..7ff9b7f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -92,6 +92,7 @@ set(APP_SOURCES "src/patterns/structural/Adapter.cpp" "src/patterns/structural/Bridge.cpp" "src/patterns/structural/Proxy.cpp" + "src/patterns/structural/Composite.cpp" ) # Test files diff --git a/docs/uml/patterns_structural_adapter.drawio.svg b/docs/uml/patterns_structural_adapter.drawio.svg index 3da6dc6..e7d1996 100644 --- a/docs/uml/patterns_structural_adapter.drawio.svg +++ b/docs/uml/patterns_structural_adapter.drawio.svg @@ -1,4 +1,4 @@ -Client+ clientCode(Target *target): typeUseTarget + request(): typeAdaptee + specificRequest(): typeExtendsAdapter- m_adaptee: Adaptee + request(): typeUserequest(){m_daptee.specificRequest();....}Newly AddedNo Change \ No newline at end of file +Client+ clientCode(Target *target): typeUseTarget + request(): typeAdaptee + specificRequest(): typeExtendsAdapter- m_adaptee: Adaptee* + request(): typeUserequest(){m_daptee.specificRequest();....}Newly AddedNo Change \ No newline at end of file diff --git a/docs/uml/patterns_structural_composite.drawio.svg b/docs/uml/patterns_structural_composite.drawio.svg new file mode 100644 index 0000000..68291ec --- /dev/null +++ b/docs/uml/patterns_structural_composite.drawio.svg @@ -0,0 +1,4 @@ + + + +<<abstract>> FileSystem # parent_: FileSystem*- name_: string+ setParent(Coponent* parent): void+ getParent(): Component* + setName(string name): void+ getName(): string + virtual isFolder(): bool+ virtual add(Component* component): void+ virtual remove(Component* component): void+ <<interface>> open(): type1AggregationClient+ clientCode(FileSystem* fs): typeUseFolder+ children: list<FileSystem* >+ add(Component* component): void+ remove(Component* component): void+ open(): typeExtends1...*AggregationFile+ open(): typeExtendsText+ open(): typeExtendsComponent# parent_: Component + setParent(Coponent* parent): void+ getParent(): Component* + <<abstract>> add(Component* component): void+ <<abstract>> remove(Component* component): void+ <<abstract>> isComposite(): bool+ <<interface>> operation(): type1AggregationClient+ clientCode(Component* c): typeUseComposite+ children: list<Component* >+ add(Component* component): void+ remove(Component* component): void+ operation(): typeExtends1...*AggregationLeaf+ operation(): typeExtendsComposite<Folder>Composite<Folder>Composite<Folder>FileFilevirtual void add(){ // }virtual void remove(){ //}virtual bool isFolder(){ return false;} \ No newline at end of file diff --git a/src/patterns/structural/Composite.cpp b/src/patterns/structural/Composite.cpp new file mode 100644 index 0000000..0c1ee60 --- /dev/null +++ b/src/patterns/structural/Composite.cpp @@ -0,0 +1,515 @@ +#include +#include +#include +#include + +namespace +{ + namespace Problem + { + class File + { + private: + std::string _name; + + public: + explicit File(const std::string &fileName) : _name{fileName} {} + std::string getName() const + { + return this->_name; + } + + void setName(const std::string &name) + { + this->_name = name; + } + + void open() const + { + std::cout << "Open file: " << _name << "\n"; + } + }; + + // [P1] Have the NewTypeFile => Update Folder (fields, functions) + + class Folder + { + private: + std::string _name; + std::list _files; // Should store the pointer to the actual file so when we delete the file it should delete in the list + std::list _subFolders; + // [P2] What about the parrent ? + + public: + explicit Folder(const std::string &name) : _name{name} + { + } + + ~Folder() + { + for (File *f : _files) + { + delete f; + } + + for (Folder *sf : _subFolders) + { + delete sf; + } + } + + void removeFile(const File *file) + { + _files.remove_if([file](const File *f) + { return f == file; }); + } + + void removeFileByName(const std::string &name) + { + + // for (auto i = _files.begin(); i != _files.end(); ++i) + // { + // if ((*i)->getName() == name) + // { + // delete (*i); + // _files.erase(i); + // return; + // } + // } + + auto it = std::find_if(_files.begin(), _files.end(), + [&name](const File *f) + { return f->getName() == name; }); + + if (it != _files.end()) + { + delete *it; // free the memory + _files.erase(it); // remove the pointer from the list + } + } + + void removeFolderByName(const std::string &name) + { + // for (auto i = _subFolders.begin(); i != _subFolders.end(); ++i) + // { + // if ((*i)->getName() == name) + // { + // delete (*i); + // _subFolders.erase(i); + // return; + // } + // } + auto it = std::find_if(_subFolders.begin(), _subFolders.end(), + [&name](const Folder *f) + { return f->getName() == name; }); + + if (it != _subFolders.end()) + { + delete *it; // free the memory + _subFolders.erase(it); // remove the pointer from the list + } + } + + void addFile(File *file) + { + _files.push_back(file); + } + + void addFolder(Folder *folder) + { + _subFolders.push_back(folder); + } + + void removeFolder(const Folder *folder) + { + _subFolders.remove_if([folder](const Folder *f) + { return f == folder; }); + } + + std::string getName() const + { + return _name; + } + + void open() const + { + std::cout << "Open Folder: " << _name << "\n"; + } + + int size() const + { + int size = static_cast(_files.size()); + + // Consider using std::accumulate algorithm instead of a raw loop. + std::for_each(_subFolders.begin(), _subFolders.end(), [&size](const Folder *sf) + { size += sf->size(); }); + + // for (const Folder *subFolder : _subFolders) + // { + // size += subFolder->size(); + // } + + return size; + } + + const std::list &getSubFolders() const + { + return _subFolders; + } + + const std::list &getFiles() const + { + return _files; + } + + void getFilesRecursive(std::list &out) const + { + out.insert(out.end(), _files.begin(), _files.end()); + for (const Folder *sf : _subFolders) + { + sf->getFilesRecursive(out); + } + } + + void getSubFoldersRecursive(std::list &out) const + { + out.insert(out.end(), _subFolders.begin(), _subFolders.end()); + for (const Folder *sf : _subFolders) + { + sf->getSubFoldersRecursive(out); + } + } + }; + + namespace Client + { + void clientCode(const Folder *folder) + { + std::cout << "File name: " << folder->getName() << "\n"; + folder->open(); + + std::cout << "Folder size: " << folder->getName() << ", size: " << folder->size() << "\n"; + std::list files; + folder->getFilesRecursive(files); + for (const File *f : files) + { + std::cout << "\t\tFile: " << f->getName() << "\n"; + } + + std::list subFolders; + folder->getSubFoldersRecursive(subFolders); + for (const Folder *sf : subFolders) + { + std::cout << "\t\tFolder: " << sf->getName() << "\n"; + } + } + } + + void run() + { + std::cout << "\n\n"; + Folder *root = new Folder("root"); + root->open(); + + // Prepare files + File *file1 = new File("fileName1.txt"); + File *file2 = new File("fileName2.txt"); + File *file3 = new File("fileName3.txt"); + root->addFile(new File(*file1)); + root->addFile(new File(*file2)); + root->addFile(new File(*file3)); + + Folder *subFolder1 = new Folder("subFolder1"); + Folder *subFolder2 = new Folder("subFolder2"); + Folder *subFolder3 = new Folder("subFolder3"); + + root->addFolder(new Folder(*subFolder1)); + root->addFolder(new Folder(*subFolder2)); + root->addFolder(new Folder(*subFolder3)); + + Client::clientCode(root); + + // Clean up memory + delete subFolder1; + delete subFolder2; + delete subFolder3; + delete file1; + delete file2; + delete file3; + + root->removeFileByName("fileName1.txt"); + Client::clientCode(root); + delete root; // deletes all files/subfolders inside recursively + } + + } + + namespace CompositePattern + { + /** + * Component is the abstraction for leafs and composites. + * It defines the interface that must be implemented by the objects in the composition. + * For example a file system resource defines move, copy, rename, and getSize methods for files and folders. + */ + class FileSystem + { + private: + FileSystem *_parent; + std::string _name; + + public: + explicit FileSystem(const std::string &fileName) : _parent{nullptr}, _name{fileName} {} + virtual ~FileSystem() = default; + + FileSystem *getParent() const + { + return _parent; + } + + void setParent(FileSystem *parent) + { + _parent = parent; + } + + std::string getName() const + { + return this->_name; + } + + void setName(const std::string &name) + { + this->_name = name; + } + + virtual void add(FileSystem *fs) + { + // do nothing here + } + + virtual void remove(FileSystem *fs) + { + // do nothing here + } + + /** + * Check if the component is composite or not + * You can provide a method that lets the client code figure out whether a + * component can bear children. + */ + virtual bool isComposite() const + { + return false; + } + + virtual void open() const = 0; + }; + + /** + * Composite - A Composite stores child components in addition to implementing methods defined by the component interface. + * Composites implement methods defined in the Component interface by delegating to child components. + * In addition composites provide additional methods for adding, removing, as well as getting components. + */ + class Folder : public FileSystem + { + private: + std::list _children; + + // Remove children + [[maybe_unused]] void removeChildren(const FileSystem *file) + { + _children.remove_if([file](const FileSystem *f) + { return f == file; }); + } + + [[maybe_unused]] void removeChildrenByName(const std::string &name) + { + + auto it = std::find_if(_children.begin(), _children.end(), + [&name](const FileSystem *f) + { return f->getName() == name; }); + + if (it != _children.end()) + { + delete *it; // free the memory + _children.erase(it); // remove the pointer from the list + } + } + + [[maybe_unused]] const std::list &getChildren() const + { + return _children; + } + + void getChildrensRecursive(std::list &out) const + { + for (FileSystem *fs : _children) + { + if (fs->isComposite()) + { + const Folder *f = static_cast(fs); + if (f != nullptr) + f->getChildrensRecursive(out); + } + else + { + out.push_back(fs); + } + } + } + + public: + explicit Folder(const std::string &name) : FileSystem{name} + { + } + + ~Folder() + { + // Delete folder should delete all children + for (FileSystem *f : _children) + { + if (f != nullptr) + delete f; + } + } + + void add(FileSystem *fs) override + { + _children.push_back(fs); + fs->setParent(this); + } + + void remove(FileSystem *fs) override + { + _children.remove(fs); + fs->setParent(nullptr); + } + + void open() const override + { + std::cout << "Open Folder: " << this->getName() << "\n"; + } + + int size() const + { + int size = static_cast(_children.size()); + std::for_each(_children.begin(), _children.end(), [&size](FileSystem *fs) + { + if(fs->isComposite()){ + const Folder* f = static_cast(fs); + if(f!= nullptr) + size += f->size(); + } }); + + return size; + } + }; + + /** + * The Leaf class represents the end objects of a composition. A leaf can't have + * any children. + * + * Usually, it's the Leaf objects that do the actual work, whereas Composite + * objects only delegate to their sub-components. + */ + class File : public FileSystem + { + public: + explicit File(const std::string &name) : FileSystem{name} + { + } + + void open() const override + { + std::cout << "Open File: " << this->getName() << "\n"; + } + }; + + class ZipFile : public FileSystem + { + public: + explicit ZipFile(const std::string &name) : FileSystem{name} + { + } + + void open() const override + { + std::cout << "Open ZipFile: " << this->getName() << "\n"; + } + }; + + class ShortCut : public FileSystem + { + private: + FileSystem *origin; + + public: + explicit ShortCut(FileSystem *fs) : FileSystem{fs->getName()} + { + origin = fs; + } + + void open() const override + { + std::cout << "Open ShortCut: " << this->getName() << "\n"; + if (origin != nullptr) + { + std::cout << "Navigate to: " << origin->getName() << "\n"; + } + else + { + std::cout << "Original file no longer exist\n"; + } + } + }; + + namespace Client + { + void clientCode(const FileSystem *fs) + { + std::cout << "File name: " << fs->getName() << "\n"; + fs->open(); + } + } + + void run() + { + std::cout << "\n\n"; + FileSystem *root = new Folder("root"); + root->open(); + + // Prepare files + FileSystem *file1 = new File("file_name.txt"); + FileSystem *file2 = new ZipFile("zipfile_name.txt"); + FileSystem *file3 = new ShortCut(file2); + root->add(file1); + root->add(file2); + root->add(file3); + Client::clientCode(root); + + Folder *subFolder1 = new Folder("subFolder1"); + Folder *subFolder2 = new Folder("subFolder2"); + Folder *subFolder3 = new Folder("subFolder3"); + root->add(subFolder1); + root->add(subFolder2); + root->add(subFolder3); + + Client::clientCode(root); + + delete root; // deletes all files/subfolders inside recursively + } + + } + +} + +struct CompositeAutoRuner +{ + CompositeAutoRuner() + { + std::cout << "\n--- Composite Pattern Example ---\n"; + Problem::run(); + CompositePattern::run(); + } +}; + +static CompositeAutoRuner instance; \ No newline at end of file