pythonによるデザインパターン[Vistor]
# Intro
この文章は結城 浩さんの「Java言語で学ぶデザインパターン入門」を、pythonで実装してみたサンプルコードです。
筆者の環境は以下の通りです。Python 3.6.3
まだ修行中の身なので間違いがあると思いますがご了承ください。 今回は「Vister」です。
# Summarise
本書のp154によると、 Vistorとは
データ構造と処理を分離
とあります。データの構造とデータの処理を分離して記述することで部品としての独立性を高め、管理しやすいプログラムになります。
# Practice
## Specification
やることはcompositeパターンと同様です。compositeパターンをまず確認してください。
前と同様に、ファイルとフォルダを模式的に表現したプログラムを作成します。ファイルを表すクラスclass File
とフォルダを表すクラスclass Folder
を用意し、さらにそれらの抽象クラスclass Entry
も考えましょう。
フォルダにファイルをどんどん追加していき( def add
)、以下の構造をもつファイルを構成していきます。
root
|-- bin
| |-- vim
| |-- latex
|
|-- tmp
|-- usr
|-- yuki
| |-- diary.html
| |-- composite.py
|
|-- hanako
| |-- memo.tex
|
|-- tomura
|-- game.doc
|-- junk.mail
唯一違う点は、フォルダを表すクラスclass Folder
のメソッド def print_line
で表示するのではなく、別の class ListVistor
というクラスの def visit
で、以下のように表示しましょう(ファイル名の後の数字はファイルサイズです)。
Making root entries
/root (30000)
/root/bin (30000)
/root/bin/vi (10000)
/root/bin/latex (20000)
/root/tmp (0)
/root/usr (0)Making user entries...
/root (31500)
/root/bin (30000)
/root/bin/vi (10000)
/root/bin/latex (20000)
/root/tmp (0)
/root/usr (1500)/root/usr/yuki (300)
/root/usr/yuki/diary.html (100)
/root/usr/yuki/composite.py (200)
/root/usr/hanako (300)
/root/usr/hanako/memo.tex (300)
/root/usr/tomura (900)
/root/usr/tomura/game.doc (400)
/root/usr/tomura/junk.mail (500)
## Explanation
前と同様に、ファイルを表すクラスclass File
・フォルダを表すクラスclass Folder
、またその抽象クラスclass Entry
を作成しましょう。
前と大きく違うのは一番上にある、抽象メソッド def accept
が実装されている、 class Element
が作られている点です。class Entry
はclass Element
を継承しているので、class File
・class Folder
は def accept
を実装する必要があります。
class File
・class Folder
のdef accept
は引数v
をとって、それのv
に対して def visit
を実行しています。後から詳しく説明しますが、def visit
の引数に自分自身のインスタンス(class File
ならclass File
自身)を指定していることに注目しておいてください。
まだ、 def visit
の説明をしていませんが、引数v
はclass ListVistor
のインスタンスです。つまり、class File
・class Folder
は def accept
を介してのみ、データの処理を行っているのです(データの構造とデータの処理の分離)。
次に今回一番重要なclass ListVistor
の説明です。class ListVistor
はメソッド def visit
しか用意されていないので、その説明だけをします。
まず、引数が file
と folder
の2つあることに注目してください。先ほど説明したように、 def visit
は、Class File
・class Folder
のdef accept
から呼ばれ、自分自身を引数にとります。つまりClass File
から呼ばれる時は file
が指定され、class Folder
から呼ばれる時は folder
が指定されます。 file
もしくは folder
のどちらが存在しているかによって、すなわちClass File
・class Folder
どちらから def visit
が呼ばれたかで条件を分岐しています。
Class File
から呼ばれた時は
(フォルダ名)/(ファイル名) (ファイルサイズ)
のように表示しているだけです。
class Folder
から呼ばれた時は、 self.currentfolder
を
self.currentfolder = self.currentfolder + '/' + folder.name
のように書き換えます。その後フォルダの中のファイルが格納してある folder.folder
から、一つづつファイルを取り出して、 def accept
を実行します。 def accept
はClass File
から def visit
を再帰的に呼び出します。
難しいことをしているようですが、要はフォルダの階層を一つ深くして、先ほど上で見た、Class File
から呼ばれた時の処理の
(フォルダ名)/(ファイル名) (ファイルサイズ)
を実行しているだけです。フォルダの階層を一つ深くしているので、結果的には
(フォルダ名)/(フォルダ名)/(ファイル名) (ファイルサイズ)
のように表示します。
まあ、ここのアルゴリズムはVisitorパターンの本質ではないのでわからなくても大丈夫です。ただし重要なことは、データ構造Class File
・class Folder
から分離されたclass ListVistor
の def visit
にデータの処理が行われていることです。ここはしっかりと確認してください。
最後にmain関数を見ましょう。
main関数では最初にフォルダを作成し、その後フォルダ・ファイルを追加していきます。重要なのは処理を実行する以下の部分です。
rootfolder.accept(ListVistor())
class Folder
の def accept
を class ListVistor
を引数として呼びます。class Folder
の内部の def accept
では、class ListVistor
の def vistor
を呼び、処理が実行されます。class Folder
がclass ListVistor
を介してデータの処理を実行していることを確認してください。
## Sample Code
class Visitor
という class ListVistor
の抽象クラスがありますが、これはただのclass ListVistor
抽象クラスで、他クラスから扱いやすくしているだけなので説明は省きます。
python3 visitor.pyMaking root entries
/root (30000)
/root/bin (30000)
/root/bin/vi (10000)
/root/bin/latex (20000)
/root/tmp (0)
/root/usr (0)Making user entries...
/root (31500)
/root/bin (30000)
/root/bin/vi (10000)
/root/bin/latex (20000)
/root/tmp (0)
/root/usr (1500)/root/usr/yuki (300)
/root/usr/yuki/diary.html (100)
/root/usr/yuki/composite.py (200)
/root/usr/hanako (300)
/root/usr/hanako/memo.tex (300)
/root/usr/tomura (900)
/root/usr/tomura/game.doc (400)
/root/usr/tomura/junk.mail (500)
# Conclusion
少し処理が複雑ですが、重要なのはclass Folder
がclass ListVistor
を介してデータの処理を実行していることと、データ構造Class File
・class Folder
から分離されたclass ListVistor
の def visit
にデータの処理が行われていることです。
これによってデータの構造とデータの処理が分離でき、部品としての独立性が高められ、メンテナンスしやすいプログラムとなります。
個人的には class ListVistor
の処理が複雑になり過ぎる恐れがあり、プログラムをパッと見たときにプログラムの内容がわかりにくくなる気がします…どうでしょうか?
# ref
- 「増補改訂版 Java言語で学ぶデザインパターン入門」 結城浩(著)第11版