Commandパターンを用いたSLA原則の適用
1. 目的
SLA原則に準拠したコードを書きたい
- SLA原則に準拠した保守性の高いコードを書きたい
- エントリーポイント/パブリック関数にあたるロジックは抽象度の高い関数/メソッドの羅列だけで処理の内容を表現したい
2. 課題
3. 解決策
処理のステップをコマンド化し、コマンドの羅列で処理のステップを表現する
関数間で共有されるデータはDTOを引き回す
- 処理のステップをコマンド化した関数はそれぞれ独立しており、状態を共有できない
- 多くの処理は前処理の結果に依存することになる為、データを共有する仕組みが必要になる
- データ共有の仕組みの為、DTO(Data Transfer Object)を導入する
class Dto:
def __init__(value, want_exit = False):
self.value = value
self.want_exit = want_exit
- DTOは保持したいデータのプロパティ(上記のサンプルコードでは
value
)と処理中断フラグ(want_exit
)を持つ
-
value
にあたるプロパティは共有されるデータの数だけいくつあっても構わない(ここでは簡単のため一個にしている)
4. メリット
SLA原則に則った抽象度
制御構造とビジネスロジックを分解できる
- 関数呼び出しや中断といったコードの制御構造とビジネスロジックを完全に分離することができる
-
exec
関数は関数の実行と中断のみ行い、ビジネスロジックの要素は存在しない
def exec(dto, *fns):
buffer = dto
for f in fns:
buffer = f(buffer)
if buffer.want_exit:
return buffer
return buffer
- 一方、ビジネスロジックを担当する関数群はビジネスロジックの実装のみを責務とし、制御構造を意識する必要はない
def f1(dto):
dto.value = dto.value + 10
return dto
def f2(dto):
dto.value = dto.value + 40
return dto
def f3(dto):
dto.value = dto.value * 2
return dto
5. デメリット
ロジックが分散するのでプログラムの理解が困難になる
6. 注意事項
処理フローが大きく変わるパラメータがある場合
- パラメータによって処理フローが大きく変わる場合はパラメータ毎の関数を作った方がよい
参考資料
Appendix-1 ソースコード全体
class State:
def __init__(value, want_exit=False):
self.value = value
self.want_exit = should_exit
def exec(state, *fns):
buffer = state
for f in fns:
buffer = f(buffer)
if buffer.should_exit:
return buffer
return buffer
def f1(state):
state.value = state.value + 10
return state
def f2(state):
state.value = state.value + 40
return state
def f3(state):
state.value = state.value * 2
return state
if __name__=="__main__":
state = State(0, False)
result = exec(
state,
f1,
f2,
f3
)
print(result.value) # => 100