Pythonで、デザインパターン「Command」を学ぶ

GoFのデザインパターンをPythonで学習してみたいと思います。

# ■ Command（コマンド・パターン）
Command パターン（英: command pattern）はオブジェクト指向プログラミングにおけるデザインパターンの一つで、動作を表現するオブジェクトを示す。Command オブジェクトは、動作とそれに伴うパラメータをカプセル化したものである。
例として、印刷を行うライブラリが PrintJob クラスを備えているとする。ライブラリのユーザーは新たに PrintJob オブジェクトを作成し、パラメータ（印刷するドキュメント、印刷部数など）をセットし、最後にプリンターにジョブを送信するメソッドを呼び出す。

## UML class and sequence diagram
![W3sDesign_Command_Design_Pattern_UML.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/103595/4290554a-b93f-bf3f-efab-eeda08b45f59.jpeg)
## UML class diagram
![command.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/103595/f3e8024e-2b00-9240-bac8-694521ad36e5.png)
[(以上、ウィキペディア（Wikipedia）より引用)](https://ja.wikipedia.org/wiki/Command_パターン)


# ■ "Command"のサンプルプログラム
実際に、Commandパターンを活用したサンプルプログラムを動かしてみて、次のような動作の様子を確認したいと思います。

- ファイル権限"777"の`test1.txt`ファイルを作成する
- ファイル権限"600"の`test2.txt`ファイルを作成する

なお、サンプルプログラムは、第一引数：作成したいファイル名、第二引数：付与したいファイル権限とします。

```console
$ python Main.py test1.txt 777
% touch test1.txt
% chmod 777 test1.txt
```
```
$ python Main.py test2.txt 600
% touch test2.txt
% chmod 600 test2.txt
```
ファイル一覧を確認します。

```console
$ ls -l|grep test
-rwxrwxrwx  1 ttsubo  staff    0  1 26 13:01 test1.txt
-rw-------  1 ttsubo  staff    0  1 26 13:01 test2.txt
```
想定どおり、ファイルが生成できました。

# ■ サンプルプログラムの詳細
Gitリポジトリにも、同様のコードをアップしています。
https://github.com/ttsubo/study_of_design_pattern/tree/master/Command

- ディレクトリ構成

```console
.
├── Main.py
└── command
    └── command.py
```
## (1) Command（命令）の役
命令のインタフェースを定める役です。
サンプルプログラムでは、`Command`クラスが、この役を努めます。

```python:command/command.py
from abc import ABCMeta, abstractmethod

class Command(metaclass=ABCMeta):
    @abstractmethod
    def execute(self):
        pass

    @abstractmethod
    def display(self):
        pass
```
## (2) ConcreteCommand（具体的命令）の役
`Command`役のインタフェースを実際に実装している役です。
サンプルプログラムでは、`FileTouchCommand`クラスと `ChmodCommand`クラスが、この役を努めます。

```python:command/command.py
class FileTouchCommand(Command):
    def __init__(self, filename, receiverObj):
        self.__filename = filename
        self.__receiver = receiverObj

    def execute(self):
        self.__receiver.createFile(self.__filename)

    def display(self):
        print("% touch {0}".format(self.__filename))


class ChmodCommand(Command):
    def __init__(self, filename, permission, receiverObj):
        self.__filename = filename
        self.__permission = permission
        self.__receiver = receiverObj

    def execute(self):
        self.__receiver.changeFileMode(self.__filename, self.__permission)

    def display(self):
        permission = format(self.__permission, 'o')
        print("% chmod {0} {1}".format(permission, self.__filename))
```
## (3) Receiver（受信者）の役
`ConcreteCommand`役の命令を実行するときに対象となる役です。命令の受け取り手と呼んでもよいでしょう。
サンプルプログラムでは、`FileOperator`クラスが、この役を努めます。

```python:command/command.py
from pathlib import Path

...(snip)

class FileOperator(object):
    def createFile(self, filename):
        Path(filename).touch()

    def changeFileMode(self, filename, permission):
        Path(filename).chmod(permission)
```

## (4) Invoker（起動者）の役
命令の実行を開始する役です。`Command`役で定義されているインタフェースを呼び出す役になります。
サンプルプログラムでは、`CompositeCommand`クラスが、この役を努めます。

```python:command/command.py
class CompositeCommand(Command):
    def __init__(self):
        self.__cmds = []

    def append_cmd(self, cmd):
        self.__cmds.append(cmd)

    def execute(self):
        for cmd in self.__cmds:
            cmd.execute()
    
    def display(self):
        for cmd in self.__cmds:
            cmd.display()
```
## (5) Client（依頼人）の役
`ConcreteCommand`役を生成し、その際にReceiver役を割り当てる役です。
サンプルプログラムでは、`startMain`メソッドが、この役を努めます。

```python:Main.py
import sys
from command.command import FileOperator, CompositeCommand, FileTouchCommand, ChmodCommand


def startMain(filename, permission):
    recv = FileOperator()
    cc = CompositeCommand()
    cc.append_cmd(FileTouchCommand(filename, recv))
    cc.append_cmd(ChmodCommand(filename, permission, recv))
    cc.execute()
    cc.display()


if __name__ == "__main__":
    startMain(sys.argv[1], int(sys.argv[2], 8))
```

# ■ 参考URL
- [PythonでCommandパターンを実装する](http://ao-log.hatenablog.com/entry/2013/11/25/223043)
- [Pythonにおけるデザインパターン ~Pyデザパタ~ / Commandパターン](https://pydp.info/GoF_dp/behavior/14_Command/index.html)
- [Command Design Pattern In Python](http://coursegalaxy.com/designpatterns/command/command_python1.htm)