週末!プログラミング部

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

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

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

Compositeパターンとは

直訳すると「複合物」や「混合物」みたいな意味でしょうか。
wikiには「ディレクトリとファイルなどのような、木構造を伴う再帰的なデータ構造を表すことができる」とあります。

このパターンはwikiにもあるとおりオブジェクトで木構造を表現するためのものです!

Compositeパターンについて考えてみる

木構造というとファイル構造が一番イメージしやすいですかね?
今回はCompositeパターンを使ってディレクトリツリーを作ってみたいと思います(・`ω´・)b

まずファイルやディレクトリのベースとなる抽象クラスEntryを用意します。

from abc import ABCMeta, abstractmethod

class Entry(metaclass=ABCMeta) :
    @abstractmethod
    def getName(self) :
        pass

    @abstractmethod
    def printList(self, prefix) :
        pass

Entryを継承してファイルを示すクラスFileを用意します。

class File(Entry):
    def __init__(self, name) -> None:
        self.__name = name

    def getName(self) -> str:
        return self.__name

    def printList(self, prefix: str) -> None:
        uri = prefix + "/" + self.__name
        print(uri)

Entryを継承してディレクトリを示すクラスDirectoryを用意します。
Directoryクラスには新しいEntryを追加するaddEntryメソッドを備えます。
FileクラスにはaddEntryメソッドがないので新しいEntryを追加できません。
似たようなコードが世の中にはいっぱいあるのでこのあたりの説明は不要ですかね(;^ω^)

class Directory(Entry):
    def __init__(self, name) -> None:
        self.__name = name
        self.__directory = [Entry]

    def getName(self) -> str:
        return self.__name

    def addEntry(self, entry: Entry) -> [Entry]:
        self.__directory.append(entry)
        return self.__directory

    def printList(self, prefix: str) -> None:
        
        uri = prefix + "/" + self.__name
        print(uri)
        
        entryNum = len(self.__directory)
        
        for i in range(entryNum):
            self.__directory[i].printList(uri)

あとはDirectoryクラスを作成してその中にDirectoryクラスやFileクラスを追加していきます。

# ディレクトリを作成
dir = Directory("ディレクトリ名")

# ファイルを追加
dir.addEntry(File("ファイル名"))

# ディレクトリを追加
dir.addEntry(Directory("ディレクトリ名"))

# 再帰的にディレクトリとファイルを追加
dir.addEntry(Directory("ディレクトリ名").addEntry(File("ファイル名")))

サンプルコード

これまでのコードの全体像です。

from abc import ABCMeta, abstractmethod

class Entry(metaclass=ABCMeta):
    @abstractmethod
    def getName(self) -> str:
        pass

    @abstractmethod
    def printList(self, prefix: str) -> None:
        pass

    def printList(self):
        pass

class File(Entry):
    def __init__(self, name) -> None:
        self.__name = name

    def getName(self) -> str:
        return self.__name

    def printList(self, prefix: str) -> None:
        uri = prefix + "/" + self.__name
        print(uri)

class Directory(Entry):
    def __init__(self, name) -> None:
        self.__name = name
        self.__directory = [Entry]

    def getName(self) -> str:
        return self.__name

    def addEntry(self, entry: Entry) -> [Entry]:
        self.__directory.append(entry)
        return self.__directory

    def printList(self, prefix: str) -> None:
        
        uri = prefix + "/" + self.__name
        print(uri)
        
        entryNum = len(self.__directory)
        
        for i in range(entryNum):
            self.__directory[i].printList(uri)

if __name__ == "__main__" :
    # rootディレクトリを作成
    rootDir = Directory("root")

    # サブフォルダを作成
    folder1 = Directory("Folder1")
    folder2 = Directory("Folder2")
    folder3 = Directory("Folder3")
    folder4 = Directory("Folder4")

    # フォルダを追加
    rootDir.addEntry(folder1)
    folder1.addEntry(folder2)
    rootDir.addEntry(folder3)
    folder3.addEntry(folder4)

    # ファイルを作成
    file1 = File("file1")
    file2 = File("file2")
    file3 = File("file3")
    file4 = File("file4")
    file5 = File("file5")
    file6 = File("file6")

    # ファイルを追加
    rootDir.addEntry(file1)
    folder1.addEntry(file2)
    folder2.addEntry(file3)
    folder3.addEntry(file4)
    folder4.addEntry(file5)
    folder4.addEntry(file6)

    # ディレクトリツリーを表示
    rootDir.printList(".")

実行結果はこんな感じです!

$ python Composite.py
./root
./root/Folder1
./root/Folder1/Folder2
./root/Folder1/Folder2/file3
./root/Folder1/file2
./root/Folder3
./root/Folder3/Folder4
./root/Folder3/Folder4/file5
./root/Folder3/Folder4/file6
./root/Folder3/file4
./root/file1