Pythonでデザインパターン実践:Iterator

環境

  • Windows 10
  • Python 3.10.1
  • VSCode

使用するソースコード

以下の公開リポジトリに置いています

GitHub - masayan1126/tao-py-py: 作業自動化用 Python ライブラリ
作業自動化用 Python ライブラリ. Contribute to masayan1126/tao-py-py development by creating an account on GitHub.

Iteratorとは

  • 繰り返す反復するなどの意味を持ち、集約したオブジェクトを列挙する手段を提供するデザインパターン
  • 何かしらの集合要素に順次にアクセスする必要がある場合に活用されたりします
  • 集約オブジェクトを生成するクラスがそれらを走査するための共通のインタフェースを定義することが可能になる
  • for文のループ変数iの役割をパターン化したものが、Iteratorパターンとも言いかえれます

実装する内容

こちらの記事で紹介したCommandパターンと組み合わせて、複数のフォルダを一括で作成するという機能を実装します

Pythonでデザインパターン実践:Command
本記事では、Pythonでcommandパターンを実践する方法について紹介しています。commandは、1つもしくは複数の命令を1つのオブジェクトで表現します。このパターンを適用することで、命令を追加が容易になり拡張性・再利用性が向上します

クラス図

  • 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学習におすすめの書籍

独習Python/山田祥寛【3000円以上送料無料】
bookfan 1号店 楽天市場店
¥ 3,300(2022/08/04 17:32時点)
タイトルとURLをコピーしました