週末!プログラミング部

ソフトウェア開発ネタを中心に自分でいろいろ調べた内容を自分の勝手な解釈で思うがままに書いくためのブログ。サンプルソースコード、API、プラットフォーム、プログラミング言語、開発環境などを調査、分析して追求いく予定です。

Pythonでデザインパターン 壱ノ型「Iterator」

今回はPythonの勉強目的でデザインパターンIteratorをやってみました。
他のパターンにつきましては、以下の目次記事をご参照ください。

Iteratorとは

直訳だと「繰り返す」や「反復」という意味になります。
wikiには「コンテナオブジェクトの要素を列挙する手段を独立させることによって、コンテナの内部仕様に依存しない反復子を提供する」とあります。

Iteratorについて考えてみる

プログラミング言語には配列、リスト、ハッシュテーブルなどのデータを1つのオブジェクト(集合体)にまとめる仕組みがあります。
しかし、データへアクセスする方法はオブジェクト毎に異なるので、オブジェクトを利用するユーザは「オブジェクト毎にデータにアクセスする処理」を書かなければいけなくなります。
例えば、以下のような感じです。

添え字を数値としてアクセスする一般的な配列
= arry[N]

添え字をキーワードとしてアクセスする連想配列
= arry["keyword"]

アクセッサーをもつオブジェクト
= arry.get(N)

などなど

そこで「共通の方法でオブジェクト(集合体)にアクセスしてデータを取り出す仕組みを提供するよ!」といっているのがIteratorパターンかなと思っています。
イメージとしては以下のような感じです。

if __name__ == '__main__':
    dataA = list(range(5))    # 0, 1, 2, 3, 4
    dataB = {"AAA" : 1, "BBB" : 2, "CCC" : 3, "DDD" : 4}

    # dataAに対するIteratorを作成
    itr  = Iterator(dataA)
    while itr.hasNext() :    # 次のデータがなくなるまで繰り返し
        print(itr.next())    # データを取り出して次へ

   # dataBに対するIteratorを作成
    itr  = Iterator(dataB)
    while itr.hasNext() :    # 次のデータがなくなるまで繰り返し
        print(itr.next())    # データを取り出して次へ

整数型の配列dataAと文字列型をキーとする連想配列集dataBがあったとします。
このイメージだと、まずdataAに対するIteratorというオブジェクトを作成しています。
dataAへのアクセスはIteratorオブジェクトが持つhasNext()とnext()というメソッドを使用して行います。
この例では、hasNext()は集合体から読み出せるデータがあるかどうかを判定する機能、 next()は集合体から次のデータを取り出す機能を提供しています。
hasNext()を使ってデータが無くなるまで繰り返しnext()を呼び出しています。
その後、集合体dataBに対するIteratorオブジェクトを作成して同様のことを行います。
これによりユーザは、dataAでもdataBでも共通の方法で集合体にアクセスできるようになります。

サンプルプログラム

以下、サンプルプログラムです。
StudentListが集合体クラスで、Studentが集合体で管理するデータクラスです。

MyStudentListはStudentListを継承しているため集合体としても機能するようにもなっており、 iterator()メソッドで自分自身を渡してMyStudentListIteratorを生成するようになっています。

MyStudentListIteratorは生成されたタイミングでMyStudentListのインスタンスを保持します。
このインスタンスを使って、next()やhasNext()で集合体を操作する機能を実現しています。

# coding: utf-8

from abc import ABC, ABCMeta, abstractmethod

# Iteratorインタフェース
class Iterator(metaclass=ABCMeta) :

    @abstractmethod
    def hasNext() :
        pass

    @abstractmethod
    def next() :
        pass

# Aggregateインタフェース
class Aggregate(metaclass=ABCMeta) :

    @abstractmethod
    def iterator() :
        pass

# データクラス
class Student(object) :

    def __init__(self, name, sex) :
        self.__name = name
        self.__sex  = sex

    def getName(self) :
        return self.__name

    def getSex(self) :
        return self.__sex

# データリストクラス(集合体)
class StudentList(object) :

    def __init__(self) :
        self.__students = []

    def add(self, student) :
        self.__students.append(student)

    def getStudentAt(self, index) :
        return self.__students[index]

    def getLastNum(self) :
        return len(self.__students)

# Aggregateインタフェースを実装したクラス
class MyStudentList(StudentList, Aggregate) :

    def __init__(self) :
        StudentList.__init__(self)

    def iterator(self) :
        return MyStudentListIterator(self)

# Iteratorインタフェースを実装したクラス
class MyStudentListIterator(Iterator) :

    def __init__(self, list) :
        self.__myStudentList = list
        self.__index = 0

    def hasNext(self) :
        if self.__myStudentList.getLastNum() > self.__index :
            return True
        else :
            return False

    def next(self) :
        student = self.__myStudentList.getStudentAt(self.__index)
        self.__index += 1
        return student

# ユースケース
if __name__ == '__main__':

    list = MyStudentList()
    list.add(Student("AAA", 1))
    list.add(Student("BBB", 0))
    list.add(Student("CCC", 1))

    itr = list.iterator()

    while itr.hasNext() :
        print(itr.next().getName())

実行結果は以下のとおりです。

$ python Iterator.py
AAA
BBB
CCC