環境
- Windows 10
- Python 3.10.1
- VSCode
Iteratorとは
- 繰り返す、反復するなどの意味を持ち、集約したオブジェクトを列挙する手段を提供するデザインパターン
- 何かしらの集合要素に順次にアクセスする必要がある場合に活用されたりします
- 集約オブジェクトを生成するクラスがそれらを走査するための共通のインタフェースを定義することが可能になる
- for文のループ変数iの役割をパターン化したものが、Iteratorパターンとも言いかえれます
実装する内容
Commandパターンと組み合わせて、複数のフォルダを一括で作成するという機能を実装します
クラス図
- IIterator
- 要素に順次アクセスする(走査する)インタフェース
- XFolderIterator
- Iteratorで定めたインタフェースを実装。走査方法・次要素の有無等の手順の詳細処理を定義。また、上記処理の対象となるオブジェクト(Aggregate)をコンストラクタで保持しておきます。
- IAggregate
- Iteratorを作り出すインタフェースを定義する
- XFolderAggregate
- Aggregateで定めたインタフェースを実装。iteratorメソッドで、自身のオブジェクトをコンストラクタ引数に「ConcreteAggregate」のオブジェクトを返します。
- コンストラクタで取り扱う集合体を定義し、その集合に要素を追加するためのメソッドやその要素数をチェックするためのメソッドを定義します
実装
まず、IIteratorクラスを抽象クラスで作成する
IIteratorクラスには、has_nextとnextメソッドをかならず実装する必要があるように指定する
import abc
class IIterator:
@abc.abstractmethod
def __str__(self):
pass
@abc.abstractmethod
def has_next(self) -> bool:
pass
def next(self):
pass
続いて、IIteratorクラスを実装する具象クラスを作成する
has_nextメソッドとnextメソッドを定義する
from shared.Domain.xfolder import XFolder
from shared.i_iterator import IIterator
from shared.i_aggregate import IAggregate
class XFolderIterator(IIterator):
def __init__(self, i_aggregate: IAggregate):
self.i_aggregate = i_aggregate
self.index = 0
def has_next(self) -> bool:
return self.index < self.i_aggregate.size()
def next(self) -> XFolder:
next = self.i_aggregate.item_at(self.index)
self.index = self.index + 1
return next
次に、IAggregateクラスを抽象クラスで作成する
import abc
from shared.i_iterator import IIterator
class IAggregate:
@abc.abstractmethod
def __init__(self):
pass
def add_item(self):
pass
def size(self) -> int:
pass
def item_at(self, index: int):
pass
def iterator(self) -> IIterator:
return IIterator(self)
ポイントは、iteratorメソッドで、Aggregateの実装クラスのオブジェクトをコンストラクタ引数として、Iteratorのオブジェクトを返すように定義します
続いて、IAggregateクラスを実装する具象クラス(XFolderAggregate)を作成
from typing import Callable, List
from shared.Domain.xfolder import XFolder
from shared.i_aggregate import IAggregate
from shared.x_folder_iterator import XFolderIterator
class XFolderAggregate(IAggregate):
def __init__(self):
self.x_folder_list = []
def add_item(self, xfolder: XFolder):
self.x_folder_list.append(xfolder)
return self.x_folder_list
def size(self) -> int:
return len(self.x_folder_list)
def item_at(self, index: int) -> XFolder:
return self.x_folder_list[index]
def iterator(self) -> XFolderIterator:
return XFolderIterator(self)
IAggregateの具象クラスのコンストラクタで具体的な集合の初期化を行います。
Aggregateは、「集約」などの意味があります。
なお、今回取り扱う集約の1つ1つの要素として指定されているXFolderクラスは以下の通り
class XFolder:
def __init__(self, base_path, folder_name):
self.base_path = base_path
self.folder_name = folder_name
def get_base_path(self):
return self.base_path
def set_base_path(self, base_path):
self.base_path = base_path
return self
def get_folder_name(self):
return self.folder_name
def set_folder_name(self, folder_name):
self.folder_name = folder_name
return self
def get_folder_path(self):
return self.base_path + self.folder_name
実際に呼び出してみる
コマンド生成→レシーバーのセット→Aggregateを初期化→フォルダオブジェクトのリストを作成→AggregateからIteratorを生成→whileの中でhas_nextがfalseになるまで、コマンドを実行する、という流れです
※事前に作成するフォルダ名のリスト(folder_name_list)と、作成するフォルダの共通パス(base_path)を定義しておきます
・・・割愛
folder_name_list = ・・・
base_path = ""
command: ICommand = MakeFolderCommand()
command.set_reciver(MakeFolderReciver())
aggregate: IAggregate = XFolderAggregate()
xfolder_list = [
aggregate.add_item(XFolder(base_path, folder_name))
for folder_name in folder_name_list
]
iterator: IIterator = aggregate.iterator()
while iterator.has_next():
item = iterator.next()
command.execute(item)
Pythonでは、for文は上記のように[]の中に簡潔に記述することが可能です。これをリスト内包表記といいます
まとめ
いかがでしたでしょうか。本記事では、PythonでIteratorパターンを実践する方法について紹介しています。Iteratorは、繰り返す、反復するなどの意味を持ち、集約したオブジェクトを列挙する手段を提供するデザインパターンです。何かしらの集合要素に順次にアクセスする必要がある場合に活用されたりします