DP.10:複雑な裏側の処理にはシンプルなフロントエンドIFを設置する。- Facade(ファサード) -【Python】
【1】Facadeパターン概要
ざっくりいうと、「プログラム実行(アプリ起動)にあたり、裏側では複数のオブジェクトメソッドをあれこれコールする必要があるが、コード上は1つのオブジェクトからコールする」ような書き方。
(※ようは高水準のAPI・インターフェースを用意する書き方)
例えば、
【2】例:マイクロカーネル風システム
OSクラスに「ファイルサービス(ファイル操作関連)クラス」や「プロセスサービス(その他の処理関連)」を実行させることを考えてみる。
■Facade未適用版
from enum import Enum
State = Enum('State', 'new running sleeping restart zombie')
###### ファイルサービスクラス
class FileServer:
def __init__(self):
self.name = 'FileServer'
self.state = State.new
def boot(self):
print(f'booting the {self}')
self.state = State.running
def kill(self, restart=True):
print(f'killing {self}')
self.state = State.restart if restart else State.zombie
def create_file(self, user, name, permissions):
print(f"creating file '{name}', user '{user}' with permisions '{permissions}' ")
###### プロセスサービスクラス
class ProcessServer:
def __init__(self):
self.name = 'ProcessServer'
self.state = State.new
def boot(self):
print(f'booting the {self}')
self.state = State.running
def kill(self, restart=True):
print(f'Killing {self}')
self.state = State.restart if restart else State.zombie
def create_process(self, user, name):
print(f"creating process '{name}', for user '{user}'")
## 動作テスト
def main():
fs = FileServer()
ps = ProcessServer()
fs.boot()
ps.boot()
print('------')
#各サービスに対するメソッドコール
fs.create_file('fz5050','hello.txt','-rw-r-r')
ps.create_process('fz9999','ls /tmp')
if __name__ == '__main__':
main()
▲システムとしてすべて起動したいオブジェクトではあるが、1つ1つ記述してオブジェクト生成、メソッドコールをしている。
↓
これをFacadeパターンで書き直してみる。
【3】Facadeパターン用オブジェクトの用意
今回は「OperatingSystemオブジェクト」を用意して、このオブジェクト経由で各オブジェクトを操作する。
####### facade(ファサード)パターンのフロントエンドになるクラス
class OperatingSystem:
def __init__(self):
self.fs = FileServer()
self.ps = ProcessServer()
# 各クラスのbootをまとめて起動
def start(self):
[ i.boot() for i in (self.fs, self.ps)]
# FileServerオブジェクトの処理をコール
def create_file(self, user, name, permissions):
return self.fs.create_file(user, name, permissions)
# ProcessServerオブジェクトの処理をコール
def create_process(self,user, name):
return self.ps.create_process(user,name)
■使用例
## 動作テスト
def main():
os = OperatingSystem()
os.start() # bootメソッドをコール
print('------')
#各サービスに対するメソッドコール
os.create_file('fz5050','hello.txt','-rw-r-r')
os.create_process('fz9999','ls /tmp')
▲OperatingSystemオブジェクトを経由させることで、記述量が少なくなり、必要となる関連オブジェクトコールもまとめられる。
■追加修正ポイント
今回は「手動で、共通している処理のメソッド名は統一」して、フロントエンド側の記述が少し楽になるようにした。
この部分は「abc(abstract base classe)」を使うことで、メソッド名の統一を強制させることもできる。
from abc import ABCMeta, abstractmethod
... ...
class Server(metaclass = ABCMeta):
def __init__(self):
pass
def __str__(self):
return self.name
@abstractmethod
def boot(self):
pass
@abstractmethod
def kill(self, restart=True):
pass
... ...
###### ファイルサービスクラス
class FileServer(Server):
...(略)...
###### プロセスサービスクラス
class ProcessServer(Server):
...(略)...
【4】全体コード
from enum import Enum
from abc import ABCMeta, abstractmethod
State = Enum('State', 'new running sleeping restart zombie')
###### 色々なServerクラスのベースとなる抽象クラス
###### facadeパターンのために最低限必要となるメソッドを決めておく
class Server(metaclass = ABCMeta):
def __init__(self):
pass
def __str__(self):
return self.name
@abstractmethod
def boot(self):
pass
@abstractmethod
def kill(self, restart=True):
pass
###### ファイルサービスクラス
class FileServer(Server):
def __init__(self):
self.name = 'FileServer'
self.state = State.new
def boot(self):
print(f'booting the {self}')
self.state = State.running
def kill(self, restart=True):
print(f'killing {self}')
self.state = State.restart if restart else State.zombie
def create_file(self, user, name, permissions):
print(f"creating file '{name}', user '{user}' with permisions '{permissions}' ")
###### プロセスサービスクラス
class ProcessServer(Server):
def __init__(self):
self.name = 'ProcessServer'
self.state = State.new
def boot(self):
print(f'booting the {self}')
self.state = State.running
def kill(self, restart=True):
print(f'Killing {self}')
self.state = State.restart if restart else State.zombie
def create_process(self, user, name):
print(f"creating process '{name}', for user '{user}'")
####### facade(ファサード)パターンのフロントエンドになるクラス
class OperatingSystem:
def __init__(self):
self.fs = FileServer()
self.ps = ProcessServer()
# 各クラスのbootをまとめて起動
def start(self):
[ i.boot() for i in (self.fs, self.ps)]
# FileServerオブジェクトの処理をコール
def create_file(self, user, name, permissions):
return self.fs.create_file(user, name, permissions)
# ProcessServerオブジェクトの処理をコール
def create_process(self,user, name):
return self.ps.create_process(user,name)
## 動作テスト
def main():
os = OperatingSystem()
os.start() # bootメソッドをコール
print('------')
#各サービスに対するメソッドコール
os.create_file('fz5050','hello.txt','-rw-r-r')
os.create_process('fz9999','ls /tmp')
if __name__ == '__main__':
main()
複雑にオブジェクト同士が紐づいているシステムを使用するときに、facade(ファサード)パターンを適用すれば、シンプルなインターフェースをクライアントコード側に提供できる。また、システムの複雑さを気にする負担が軽減するかもしれない。
もっと応援したいなと思っていただけた場合、よろしければサポートをおねがいします。いただいたサポートは活動費に使わせていただきます。