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): type
Use
Target
 
+ request(): type
Adaptee
 
+ specificRequest(): type
Extends
Adapter
- m_adaptee: Adaptee 
+ request(): type
Use
request(){
m_daptee.specificRequest();
....
}
Newly Added
No Change
\ No newline at end of file +
Client
+ clientCode(Target *target): type
Use
Target
 
+ request(): type
Adaptee
 
+ specificRequest(): type
Extends
Adapter
- m_adaptee: Adaptee* 
+ request(): type
Use
request(){
m_daptee.specificRequest();
....
}
Newly Added
No 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(): type


1
Aggregation
Client
+ clientCode(FileSystem* fs): type
Use
Folder
+ children: list<FileSystem* >
+ add(Component* component): void
+ remove(Component* component): void

+ open(): type
Extends
1...*
Aggregation
File
+  open(): type

Extends
Text
+  open(): type
Extends
Component
# parent_: Component
+ setParent(Coponent* parent): void
+ getParent(): Component* 

+ <<abstract>> add(Component* component): void
+ <<abstract>> remove(Component* component): void
+ <<abstract>> isComposite(): bool
+ <<interface>> operation(): type

1
Aggregation
Client
+ clientCode(Component* c): type
Use
Composite
+ children: list<Component* >
+ add(Component* component): void
+ remove(Component* component): void
+ operation(): type
Extends
1...*
Aggregation
Leaf
+  operation(): type

Extends
Composite<Folder>
Composite<Folder>
Composite<Folder>
File
File
virtual 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