diff --git a/CMakeLists.txt b/CMakeLists.txt index 8e974cb..eb4f7b3 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -100,6 +100,7 @@ set(APP_SOURCES "src/patterns/structural/Decorator.cpp" "src/patterns/behavioral/ChainOfCommand.cpp" "src/patterns/behavioral/Command.cpp" + "src/patterns/behavioral/Iterator.cpp" ) # Test files diff --git a/docs/uml/patterns_behavioral_iterator.drawio.svg b/docs/uml/patterns_behavioral_iterator.drawio.svg new file mode 100644 index 0000000..ffa0cf9 --- /dev/null +++ b/docs/uml/patterns_behavioral_iterator.drawio.svg @@ -0,0 +1,4 @@ + + + +Client+ clientCode(IAggregate<T> * collection): type<<Interface>>IAggregate+ createIterator: IIterator*<<Interface>>IIterator+ hasNext(): bool+ next(): *TVectorConcreteIterator- m_container: const std::vector<T>* const- size_t m_currentIndex{0};+ hasNext(): bool+ next(): *TListConcreteIterator+ m_org_container:const std::vector<T>* const- size_t m_currentIndex{0};+ hasNext(): bool+ next(): *TVectorConcreteAggregate- m_container: std::vector<T> + add(T i): void+ createIterator: IIterator*ListConcreteAggregate- m_container: std::list<T> + add(T i): void+ createIterator: IIterator*UseUse<<create>><<create>><<create>>11ExtendsExtendsExtendsExtendscreateIterator:{return new VectorConcreteIterator<T>(m_data);}hasNext:{return m_currentIndex < m_container.size();}next:{ if (hasNext()) { return (T *)&m_data[m_currentIndex++];  } else { return nullptr;  }}clientCode:{IIterator<T> *iterator = collection->createIterator();if (iterator != nullptr) { while (iterator->hasNext()) { const T* child = iterator->next(); }  }delete iterator;} \ No newline at end of file diff --git a/src/patterns/behavioral/Iterator.cpp b/src/patterns/behavioral/Iterator.cpp new file mode 100644 index 0000000..4ba2cd2 --- /dev/null +++ b/src/patterns/behavioral/Iterator.cpp @@ -0,0 +1,239 @@ +// Iterator is a behavioral design pattern that lets you traverse elements of a collection without exposing its underlying representation (list, stack, tree, etc.). +// Provide a way to access the elements of an aggregate object sequentially without exposing its underlying representation. +// Appicability: +// (*) when your collection has a complex data structure under the hood, but you want to hide its complexity from clients (either for convenience or security reasons). +// (**) reduce duplication of the traversal code across your app. +// (***) when you want your code to be able to traverse different data structures or when types of these structures are unknown beforehand. + +// UML: docs/uml/patterns_behavioral_iterator.drawio.svg + +#include +#include +#include +#include +#include + +namespace +{ + namespace Iterator + { + class DataModel + { + private: + int m_value; + + public: + explicit DataModel(int value) : m_value{value} {} + + void setValue(int v) + { + m_value = v; + } + + int getValue() const { return m_value; } + }; + + /** + * Iterator interface: declares the operations required for traversing a collection: fetching the next element, retrieving the current position, restarting iteration, etc. + */ + template + class IIterator + { + public: + virtual bool hasNext() const = 0; + virtual const T *next() = 0; + virtual ~IIterator() = default; + }; + + /** + * Aggregate interface: declares one or multiple methods for getting iterators compatible with the collection. + * Note that the return type of the methods must be declared as the iterator interface so that the concrete collections can return various kinds of iterators. + */ + template + class IAggregate + { + public: + virtual IIterator *createIterator() = 0; + virtual ~IAggregate() = default; + }; + + /** + * Concrete Iterator: implement specific algorithms for traversing a collection. + * The iterator object should track the traversal progress on its own. This allows several iterators to traverse the same collection independently of each other. + */ + template + class VectorConcreteIterator : public IIterator + { + private: + const std::vector &m_data; + size_t m_currentIndex{0}; + + public: + explicit VectorConcreteIterator(const std::vector &data) : m_data{data} {} + + bool hasNext() const override + { + return m_currentIndex < m_data.size(); + } + + const T *next() override + { + if (hasNext()) + { + return (T *)&m_data[m_currentIndex++]; + } + else + { + return nullptr; + } + } + }; + + template + class ListConcreteIterator : public IIterator + { + private: + const std::list &m_data; + typename std::list::const_iterator m_it; + + public: + explicit ListConcreteIterator(const std::list &data) + : m_data(data), m_it(m_data.begin()) {} + + bool hasNext() const override + { + return m_it != m_data.end(); + } + + const T *next() override + { + if (!hasNext()) + return nullptr; + const T *ptr = &(*m_it); + ++m_it; + return ptr; + } + }; + + /** + * Concrete Aggregate: return new instances of a particular concrete iterator class each time the client requests one. + */ + template + class ListConreteAggregate : public IAggregate + { + private: + std::list m_data; + + public: + void add(const T &i) + { + m_data.push_back(i); + } + + IIterator *createIterator() override + { + return new ListConcreteIterator(m_data); + } + }; + + template + class VectorConcreteAggregate : public IAggregate + { + private: + std::vector m_data; + + public: + void add(const T &i) + { + m_data.push_back(i); + } + + IIterator *createIterator() override + { + return new VectorConcreteIterator(m_data); + } + }; + + /** + * The Client works with both collections and iterators via their interfaces. + * This way the client isn’t coupled to concrete classes, allowing you to use various collections and iterators with the same client code. + */ + namespace Client + { + + void clientCode(IAggregate *collection) + { + IIterator *iterator = collection->createIterator(); + + if (iterator != nullptr) + { + while (iterator->hasNext()) + { + std::cout << "int: " << *(iterator->next()) << "\n"; + } + } + + delete iterator; + } + + void clientCode(IAggregate *collection) + { + IIterator *iterator = collection->createIterator(); + + if (iterator != nullptr) + { + while (iterator->hasNext()) + { + std::cout << "data: " << iterator->next()->getValue() << "\n"; + } + } + delete iterator; + } + } + + void run() + { + std::cout << "\nVectorConcreteAggregate\n"; + VectorConcreteAggregate intCollection; + for (int i = 0; i < 10; ++i) + { + intCollection.add(i); + } + Client::clientCode(&intCollection); + std::cout << "\n"; + VectorConcreteAggregate dataCollection; + for (int i = 0; i < 10; ++i) + { + dataCollection.add(DataModel(i * 10)); + } + Client::clientCode(&dataCollection); + + std::cout << "\nListConreteAggregate\n"; + ListConreteAggregate intCollection2; + for (int i = 0; i < 10; ++i) + { + intCollection2.add(i); + } + + Client::clientCode(&intCollection2); + std::cout << "\n"; + ListConreteAggregate dataCollection2; + for (int i = 0; i < 10; ++i) + { + dataCollection2.add(DataModel(i * 10)); + } + Client::clientCode(&dataCollection2); + } + } +} + +struct IteratorAutoRunner +{ + IteratorAutoRunner() + { + std::cout << "\n--- Iterator Pattern Example ---\n"; + Iterator::run(); + } +}; + +static IteratorAutoRunner instance; \ No newline at end of file
<<Interface>>IAggregate
+ createIterator: IIterator*
<<Interface>>IIterator
+ hasNext(): bool
+ next(): *T