Behavioral iterator pattern

The function of iterator: traverse a collection of the same elements.

The iterator pattern is a behavioral design pattern.

The idea of iterator: traverse the values of the elements in the collection in some way without changing the underlying elements.

Implementation of iterator:

1: premise: a collection element class, a collection class, and an iterator class are required

2: the iterator can obtain the object of the collection class and encapsulate the interface of the object.

3: how to extend iterator model: you can extend it with template model

reference resources https://www.cnblogs.com/bugxch/ To understand the iterator pattern:

1: The example of traversing the books in the bookshelf in the graphic design pattern is the UML diagram:

[the external chain picture transfer fails. The source station may have an anti-theft chain mechanism. It is recommended to save the picture and upload it directly (img-XahJ4p33-1627281881798)(... \ md document related pictures \ iterator mode instance UML diagram. png)]

To further illustrate the above figure,

  • Aggregate represents the interface of the collection. The bookshelf implements this interface, so the bookshelf must have an iterator method;
  • Iterator represents the interface of the iterator of the collection. The iterator of the bookshelf needs to use the specific object of the bookshelf to call the relevant methods to implement it;
    It should be noted that the iterator here has only one forward iterator, and you can also define an iterator for backward traversal.

Understanding: Aggregate and Iterator are abstract classes and virtual interfaces;

Bookshelf is the implementation class of the actual collection class Initialize iterator class

BookShelfIterator is the specific iterator encapsulation implementation class corresponding to the bookshelf collection class. = = " The object to be passed into the collection class during initialization

2: According to the above understanding, a preliminary attempt is made to implement the demo code (C + +) of the iterator:

//1: A class that implements a single element in a collection
//2: The class that implements the iterator. The class of the specific iterator can operate the corresponding collection class and use the relevant traversal methods in the collection class
//How to implement the interface of the collection class object that can be used in the iterator needs to be passed
//3: The class that implements the collection, including the interface to create the iterator. 


//Simply a collection class, and then create an iterator class to control the traversal of the collection and access the elements in the collection through the iterator class.
#include <iostream>
#include <vector>
using namespace std;

class TestIterator;
class TestVector{
public:
	//Tectonics and Deconstruction
	TestVector():m_itr(nullptr), count(0)
	{}
	~TestVector() {
		if(m_itr != nullptr)
		{
			delete m_itr;
			m_itr = nullptr;
		}
	}
	//Class itself inserts and gets elements
	void push(int d) {
		m_testvector.push_back(d);
		count++;
	}

	void pop()
	{
		m_testvector.pop_back();
		count--;
	}

	int getCount()
	{
		return count;
	}

	int getData(int d)
	{
		if(d < count)
		{
			return m_testvector[d];
		}
		return -1;
	}
	//The method of creating iterators is defined here, and the access of array elements is controlled through iterators
	TestIterator * CreateTestIterator()
	{
		if(m_itr == nullptr)
		{
			m_itr = new TestIterator(this);
		}
		return m_itr;
	}

private:
	vector<int> m_testvector;
	TestIterator * m_itr; //Here in order to correspond to the release
	int count;
};

//The corresponding method is called through the iterator class to access the collection elements. Here, the access is controlled by subscripts
class TestIterator
{
public:
	//The class and collection subscript of the collection to operate on
	TestIterator(TestVector * ts):m_ts(ts), curr(0)
	{}
	~TestIterator() {}

	//Determines whether there is a next element
	bool HasNext()
	{
		if(curr >= m_ts->getCount())
		{
			return false;
		}
		return true;
	}

	//What is returned is actually the next element of the iterator in the collection class
	int Next()
	{
		int data = m_ts->getData(curr);
		curr++;
		return data;
	}
	//Reset node information
	void reset()
	{
		curr=0;
	}
private:
	TestVector* m_ts;
	int curr;
};

int main()
{
	//Create a collection class and insert the relevant data
	TestVector * test = new TestVector();
	test->push(5);
	test->push(8);
	test->push(6);
	test->push(7);
	test->push(3);
	test->push(2);
	cout<<"count:"<<test->getCount()<<endl;
	TestIterator *itr = test->CreateTestIterator();
	int num = 0;
	//The element here is an int, so it can be printed directly. There can be other types
	while(itr->HasNext())
	{
		cout<<" num "<<num <<"is "<<itr->Next()<<endl;
		num ++;
	}

	for(int i=0;i<test->getCount(); i++)
	{
		cout<<" i "<<i<<" is" <<test->getData(i)<<endl;
	}

	if(test != nullptr)
	{
		delete test;
		test= nullptr;
	}
	return 0;
}

Note: it is found that the above code cannot be compiled because the two classes refer to each other.

3: Think about solving the problem of reference compilation

It can be treated with virtual basis functions.

You can use C + + template definition to avoid it and directly replace it with type.

//In the above demo, it is found that the compilation fails, which is the reason for the related references between classes, so an intermediate class is needed to implement it

//The initialization of iterator test class needs to use collection class. Here, a base class of collection class is constructed to realize the initialization test of iterator
//The collection class needs to use the iterator class for related initialization. It also needs to construct an iterator basic class for the collection class interface to call
#include <iostream>
#include <vector>
using namespace std;

class baseIterator{
public:
	virtual ~baseIterator() = default;
	virtual bool HasNext() = 0;
	virtual int Next() = 0;
	virtual void reset() = 0;
};

//The method containing the necessary collection class, which is passed in to implement the call of the iterator
class baseTestIterator{
public:
	virtual ~baseTestIterator() = default;
	virtual baseIterator * CreateTestIterator()= 0;
	virtual int getCount() = 0;
	virtual int getData(int d) = 0;
};

class TestIterator: public baseIterator
{
public:
	//The class and collection subscript of the collection to operate on
	TestIterator(baseTestIterator * ts):m_ts(ts), curr(0)
	{}
	~TestIterator() {}

	//Determines whether there is a next element
	bool HasNext()
	{
		if(curr >= m_ts->getCount())
		{
			return false;
		}
		return true;
	}

	//What is returned is actually the next element of the iterator in the collection class
	int Next()
	{
		int data = m_ts->getData(curr);
		curr++;
		return data;
	}
	//Reset node information
	void reset()
	{
		curr=0;
	}
private:
	baseTestIterator* m_ts;
	int curr;
};

class TestVector: public baseTestIterator{
public:
	//Tectonics and Deconstruction
	TestVector():m_itr(nullptr), count(0)
	{}
	~TestVector() {
		if(m_itr != nullptr)
		{
			delete m_itr;
			m_itr = nullptr;
		}
	}
	//Class itself inserts and gets elements
	void push(int d) {
		m_testvector.push_back(d);
		count++;
	}

	void pop()
	{
		m_testvector.pop_back();
		count--;
	}

	int getCount()
	{
		return count;
	}

	int getData(int d)
	{
		if(d < count)
		{
			return m_testvector[d];
		}
		return -1;
	}
	//The method of creating iterators is defined here, and the access of array elements is controlled through iterators
	baseIterator * CreateTestIterator()
	{
		if(m_itr == nullptr)
		{
			m_itr = new TestIterator(this);
		}
		return m_itr;
	}

private:
	vector<int> m_testvector;
	baseIterator * m_itr; //Here in order to correspond to the release
	int count;
};

int main()
{
	//Create a collection class and insert the relevant data
	TestVector * test = new TestVector();
	test->push(5);
	test->push(8);
	test->push(6);
	test->push(7);
	test->push(3);
	test->push(2);
	cout<<"count:"<<test->getCount()<<endl;
	baseIterator *itr = test->CreateTestIterator();
	int num = 0;
	//The element here is an int, so it can be printed directly. There can be other types
	while(itr->HasNext())
	{
		cout<<" num "<<num <<"is "<<itr->Next()<<endl;
		num ++;
	}

	for(int i=0;i<test->getCount(); i++)
	{
		cout<<" i "<<i<<" is " <<test->getData(i)<<endl;
	}

	if(test != nullptr)
	{
		delete test;
		test= nullptr;
	}
	return 0;
}

//The iterator implementation of related set class data structures in C + + is similar, except that the template definition is added, and the next processing uses the pointer address + 1 instead of the template mechanism instead of the virtual basis function to handle the reference problem

Keywords: C++ Design Pattern

Added by devx on Fri, 14 Jan 2022 00:16:18 +0200