【迭代器設計模式詳解】C/Java/JS/Go/Python/TS不同語言實現

刀法如飛發表於2023-04-17

簡介

迭代器模式(Iterator Pattern),是一種結構型設計模式。給資料物件構建一套按順序訪問集合物件元素的方式,而不需要知道資料物件的底層表示。

迭代器模式是與集合共存的,我們只要實現一個集合,就需要同時提供這個集合的迭代器,就像Java中的Collection,List、Set、Map等,這些集合都有自己的迭代器。假如我們要實現一個這樣的新的容器,就可以引入迭代器模式,給我們的容器實現一個迭代器。

 

作用

  1. 可以提供多種遍歷物件的方式,把元素之間查詢呼叫的責任交給迭代器,而不是聚合物件。
  2. 分離了集合物件的遍歷行為,抽象出一個迭代器類來負責,這樣既可以做到不暴露集合的內部結構,又可讓外部程式碼透明地訪問集合內部的資料。

 

實現步驟

  1. 建立迭代器介面,定義hasNext()和next()方法
  2. 建立資料容器介面,用來建立迭代器
  3. 建立具體資料列表,實現資料容器介面,可以建立迭代器,內含資料列表物件
  4. 建立某種資料物件的迭代器,實現hasNext()以及next()方法,並且關聯上資料物件列表

 

UML

 

 

Java程式碼

 

迭代器抽象介面

 

// Iterator.java 迭代器抽象介面,提供next和hasNext方法
public interface Iterator {
   public boolean hasNext();
   public Object next();
}

 

 

具體迭代器

 

// ObjectIterator.java 物件迭代器,實現了抽象迭代器的方法,聚合了物件列表
public class ObjectIterator implements Iterator {

   private ObjectList objectList;

   int index;

   public ObjectIterator(ObjectList objectList) {
      this.objectList = objectList;
   }

   @Override
   public boolean hasNext() {
      if (index < objectList.size()) {
         return true;
      }
      return false;
   }

   @Override
   public Object next() {
      if (this.hasNext()) {
         // 返回資料物件提供的get方法,每訪問一次則增加下標
         return objectList.get(index++);
      }
      return null;
   }
}
 

 

資料容器介面

 

// Container.go 建立抽象容器介面,建立一個迭代器
public interface Container {
   public Iterator createIterator();
}

 

 

具體資料物件

 

// ObjectList.java 物件列表,是一種資料容器,可以建立一個迭代器
public class ObjectList implements Container {
   private Object[] objects = { "Google", "Apple", "Amazon" };

   @Override
   public Iterator createIterator() {
      System.out.println(this.getClass().getName() + "::createIterator() [獲取迭代器 ObjectIterator]");
      // 把當前物件傳給迭代器
      return new ObjectIterator(this);
   }

   public void setObjects(Object[] objects) {
      this.objects = objects;
   }

   public int size() {
      return objects.length;
   }

   public Object get(int index) {
      return objects[index];
   }
}
 

 

測試呼叫

 

    /*
     * 迭代器模式是給資料容器建立單獨的迭代器,用來遍歷裡面的資料物件
     * 資料容器和迭代器相互關聯,外部透過迭代器來訪問資料容器
     * 透過這種方式由迭代器類來負責資料遍歷,這樣可以做到不暴露集合的內部結構
     */

    int i = 0;
    ObjectList objectList = new ObjectList();
    objectList.setObjects(new String[] { "Thomas", "Merry", "Jack", "Tony", "Jerry", "Joey" });
    // for迴圈迭代物件
    for (Iterator iter = objectList.createIterator(); iter.hasNext();) {
      String name = (String) iter.next();
      System.out.println("objectList[" + i + "] = " + name);
      i++;
    }

    // while迴圈迭代物件
    Iterator iter2 = objectList.createIterator();
    objectList.setObjects(new Integer[] { 3, 5, 7, 9, 11 });
    while (iter2.hasNext()) {
      System.out.println(iter2.next());
    }
 

 

Go程式碼

 

迭代器抽象介面

 

// Iterator.go 迭代器抽象介面,提供next和hasNext方法
type Iterator interface {
  HasNext() bool
  Next() string
}

 

 

具體迭代器

 

// ObjectIterator.go 物件迭代器,實現了抽象迭代器的方法,聚合了物件列表
type ObjectIterator struct {
  // 迭代器索引
  index int
  // 聚合了資料物件
  objectList *ObjectList
}

func (o *ObjectIterator) HasNext() bool {
  if o.index < o.objectList.Size() {
    return true
  }
  return false
}

func (o *ObjectIterator) Next() string {
  if o.HasNext() {
    // 返回資料物件提供的get方法,每訪問一次下標增加1位
    item := o.objectList.Get(o.index)
    o.index += 1
    return item
  }
  return ""
}

 

 

資料容器介面

 

// Container.go 建立抽象容器介面,建立一個迭代器
type Container interface {
  CreateIterator() Iterator
}

 

 

具體資料物件

 

// ObjectList.go 物件列表,是一種資料容器,可以建立一個迭代器
type ObjectList struct {
  // 內部的資料結構
  objects []string
}

func (o *ObjectList) CreateIterator() Iterator {
  fmt.Println("ObjectList::CreateIterator() [獲取迭代器 ObjectIterator]")
  // 建立迭代器例項,繫結新建當前物件
  return &ObjectIterator{
    objectList: o,
  }
}

func (o *ObjectList) SetObjects(objects []string) {
  o.objects = objects
}

func (o *ObjectList) GetObjects() []string {
  return o.objects
}

func (o *ObjectList) Size() int {
  return len(o.objects)
}

func (o *ObjectList) Get(index int) string {
  return o.objects[index]
}
 

 

測試呼叫

 

    /*
     * 迭代器模式是給資料容器建立單獨的迭代器,用來遍歷裡面的資料物件
     * 資料容器和迭代器相互關聯,外部透過迭代器來訪問資料容器
     * 透過這種方式由迭代器類來負責資料遍歷,這樣可以做到不暴露集合的內部結構
     */

    int i = 0;
    ObjectList objectList = new ObjectList();
    objectList.setObjects(new String[] { "Thomas", "Merry", "Jack", "Tony", "Jerry", "Joey" });
    // for迴圈迭代物件
    for (Iterator iter = objectList.createIterator(); iter.hasNext();) {
      String name = (String) iter.next();
      System.out.println("objectList[" + i + "] = " + name);
      i++;
    }

    // while迴圈迭代物件
    Iterator iter2 = objectList.createIterator();
    objectList.setObjects(new Integer[] { 3, 5, 7, 9, 11 });
    while (iter2.hasNext()) {
      System.out.println(iter2.next());
    }
 

 

C語言簡版

 

#include <stdio.h>
#include <stdlib.h>

// 簡單版C語言迭代器模式,自己構建List資料型別

// 資料結構,這裡使用連結串列作為示例
struct List
{
  char *data;
  struct List *next;
};

// 迭代器結構體
struct Iterator
{
  struct List *current;
  int (*has_next)(struct Iterator *);        // 判斷是否還有下一個元素
  char *(*next)(struct Iterator *, char **); // 獲取下一個元素
};

// 判斷是否還有下一個元素
int has_next(struct Iterator *iter)
{
  return iter->current != NULL;
}

// 獲取下一個元素
char *next(struct Iterator *iter, char **value)
{
  if (iter->current == NULL)
  {
    return NULL;
  }
  *value = iter->current->data;
  iter->current = iter->current->next;
  return *value;
}

// 初始化迭代器
void create_iterator(struct Iterator *iter, struct List *head)
{
  iter->current = head;
  iter->has_next = &has_next;
  iter->next = &next;
}

// 遍歷連結串列
void iterate_list(struct List *head)
{
  struct Iterator iter;
  char *value;
  create_iterator(&iter, head);
  while (iter.has_next(&iter))
  {
    iter.next(&iter, &value);
    printf("\r\n %s ", value);
  }
  printf("\n");
}

int main()
{
  printf("test start:\r\n");
  // 構造一個連結串列
  struct List *head = (struct List *)malloc(sizeof(struct List));
  head->data = "Tom";
  head->next = (struct List *)malloc(sizeof(struct List));
  head->next->data = "Jerry";
  head->next->next = (struct List *)malloc(sizeof(struct List));
  head->next->next->data = "Max";
  head->next->next->next = NULL;

  // 使用迭代器遍歷連結串列
  iterate_list(head);

  // 釋放連結串列記憶體
  while (head != NULL)
  {
    struct List *temp = head;
    head = head->next;
    free(temp);
  }

  return 0;
}

 

 

更多語言版本

不同語言實現設計模式:https://github.com/microwind/design-pattern

相關文章