Context Object
1. 目的
レイヤーをまたいだデータ共有の仕組み
- 複数のレイヤーを持つアプリケーションで、各レイヤー間でデータ(情報)を共有する必要がある
- レイヤー間で共有したいデータを透過的に扱う方法が必要である
2. 課題
レイヤー間で共有するデータを透過的に扱う必要がある
- レイヤー間で異なるデータ構造を扱う場合、レイヤー間でのデータ受け渡しが煩雑になる
- 煩雑なデータ受け渡しロジックは不具合の原因になる
- したがって、煩雑なデータ受け渡しロジックを必要としないレイヤー透過的な共有データが必要になる
3. 解決策
レイヤー間で共有するデータを取りまとめたデータを定義する
- レイヤー間で共有するデータをクラスや構造体などで定義(Context Object)し、各レイヤーの入出力はContext Objectを渡すことでデータの共有を行う
- モジュール結合度におけるスタンプ結合を採用することになる
- 単純化した例を↓に示す
class Context:
""" Context Object """
def __init__(self, id):
self.id = id
self.output = {}
def layer1(ctx):
layer2(ctx)
ctx.output["layer1"] = "Layer1 Completed."
return ctx
def layer2(ctx):
ctx.output["layer2"] = "Layer2 Completed."
return ctx
def main():
ctx = Context("ctx1")
ctx = layer1(ctx)
print(ctx.output["layer1"]) # => Layer1 Completed.
print(ctx.output["layer2"]) # => Layer2 Completed.
Context
クラスはContext Object
であり、レイヤー間で共有したデータや処理結果を保持する(self.output
に処理結果が保存されると考えよ)
layer1
、layer2
のそれぞれの関数はレイヤーを模したもので、それぞれをプログラムのレイヤーと見なす
main
関数はエントリーポイントとなり、Context Object
を作成してlayer1
関数に与える
Context Object
はlayer1
、layer2
関数の間を透過的に受け渡され、それぞれの処理結果を内部に保持してmain
関数に戻ってくる
Context Object
を用いることでレイヤー間のデータ共有が容易になり、煩雑なデータ受け渡しロジックも不用になる
4. メリット
シンプルな仕組みでレイヤー間のデータ共有が可能になる
- レイヤー毎にデータの受け渡しロジックを実装するよりシンプルにレイヤー間データ共有が可能になる
5. デメリット
レイヤー間の結合度が上昇する
- 共有データによってレイヤー間が結合されるので、モジュール結合としてはスタンプ結合となり、データ結合よりも強い結合度になってしまう
- モジュールの結合度が強くなることで、モジュールの再利用性が低下する(
Context Object
に依存するので)
不要なデータまでレイヤー間で共有することになる
Context Object
は全レイヤーで共有するデータになるので、特定のレイヤーでしか使わないデータも他のレイヤーに公開することになる
- その結果、意図しないデータへの依存が実装される可能性があり、
Context Object
の一部のデータを修正したくても他のレイヤーで参照している為、修正できない(またはレイヤーをまたいだ修正が必要になる)といった問題が発生する可能性がある
6. 注意
濫用は禁物
Context Object
は使いどころを見極めることが重要
- システム全体を
Context Object
でデータ共有させるといった濫用は保守性を大きく損ねるので禁忌である
- 使いどころとしてはミドルウェア機構の実装などに向いている
- Go言語のWEBフレームワークであるGinのcontext.goの設計が参考になるだろう
参考資料