勉強日記

チラ裏

PoEAA ch18 Gateway

martinfowler.com


Gateway

f:id:wand_ta:20190521000324p:plain

An object that encapsulates access to an external system or resource.

  • OOシステムはオブジェクトではない外部リソースを取り扱わねばならないことがある
  • 外部リソースはそれ専用のAPIをもつ
  • そのまま使うのはよくない
    • プログラムを理解しづらくなる
    • 将来RDBXMLに置換できなくなる
  • オブジェクトでラップしろ

How It Works

  • やることは至ってシンプルなラッピング
  • 用途を満たすシンプルなAPI(=メソッドとか)を定義し、外部リソースの操作にマッピングする
  • GatewayService Stubのよい適用場所でもある
    • 後述
  • Service Stubを適用しやすい設計にするのは重要なこと
    • テスト容易性
  • Gatewayはシンプルに保て
    • 一番大事な役割は...
      • 外部リソースのラッピング・クライアントコードの要求への適合
      • スタブの適用箇所の提供
    • これらを満足できる程度の最小のものであるべき
    • 複雑なロジックはクライアントコードに任せろ
  • コード自動生成を使うのは良いアイデア
  • 2つ以上のオブジェクトでGatewayを作るのもよい
    • バックエンドとフロントエンド
      • バックエンド: ミニマルなラッパ
      • フロントエンド: バックエンドを叩き、シンプルなAPIを提供する
        • 【補】GoFFacade Pattern
    • 外部サービスのラッピングと、クライアントコードの要求への適合が相応に複雑ならば良い
    • 単純なら分けるほどではない

When to Use It

  • 外部のもの感があり、インタフェースがぎこちない(OOPと合わない)ものがあればGateway適用を検討せよ
    • ぎこちなさをシステム全体に波及させるよりも、Gatewayで閉じ込める
    • Gatewayで包むデメリットはまずない
    • クライアントコード読みやすくなる
  • Gatewayを導入するとテストが容易になる
    • Service Stubsの明確な適用箇所
    • 外部リソースのインタフェースがGatewayで包むまでもなくOOPに適合していても、
      Service Stubの適用のためにGatewayで包むのは有用
  • Gatewayでを導入すると置換可能になる
    • 【補】クライアントコードをinterfaceに依存させれば、特定の実装には依存しなくなる
    • テスト用Gatewayへの置換も可能(後述)

f:id:wand_ta:20190521000426p:plain
別の外部サービスに置換可能

  • Mapperという別の選択肢
    • 外部リソースとの結合を切り離す
    • Gatewayよりも複雑
      • クライアントと外部リソースを互いにignorantにするため

f:id:wand_ta:20190521000451p:plain
Mapper

  • これって改めてデザインパターンとして挙げるほどのものか?
    • GoFのなにがしかと同じじゃないの?
    • 下記の理由につき区別した
      • Facadeとの違い
        • Facade
          • 複雑なAPIの単純化
          • 汎用
          • 異なるインタフェースが裏にあることを示唆
        • Gateway
          • 特定用途
          • 外部リソースのAPIへの単なるマッピングだったりする
          • 置換やテストを旨とする
      • Adapterとの違い
        • Adapter
          • 既存のインタフェースに寄せるやつ
        • Gateway
          • 既存のインタフェースは(普通)ない
          • Adapterパターンをとることもある
            • GatewayインタフェースがAdapterのインタフェース(Target)
            • 外部リソースの実装を包むのがAdapter
            • 外部リソースがAdaptee
          • この場合adapterはGatewayの実装の一部
      • Mediatorとの違い
        • Mediator
          • 各クラスはMediatorを知っている
        • Gateway
          • Gatewayに包まれる外部リソースはGatewayのことを知らない
      • 【疑問】Bridgeパターンとはどう違うの
        • 既存のインタフェースはないが、様々な実装ありきでインタフェースを作る点がそっくり
        • Bridgeパターンは「種類の継承ツリーと実装の継承ツリーとを分離する」ことを旨とするから違うのかな
        • Facade, Adapter, Mediatorよりは似てると思う

Example: A Gateway to a Proprietary Messaging Service (Java)

  • コード略(pp.468-472)
  • Gatewayのインタフェースについて
    • send('CNFRM', [1, 10, 'hoge'])
      • 汎用
    • sendConfirmation(1, 10, 'hoge')
      • 特定用途
    • 良し悪し
      • 汎用インタフェース
        • 引数の正当性が静的にわからない
          • typoでエラー出たりしそう
      • 特定用途インタフェース
        • 包んでいる外部リソースに追加変更があるたびにGatewayクラスに変更が生じる
          • 実装の単純なマッピングじゃないから
          • 自動生成じゃないから
    • 両方あるとよい
  • 戻り値から例外へのマッピング
    • exit 0: 正常終了
    • ほか: 異常終了
  • Gatewayのスタブ
    • テスト用protectedメソッドを生やしておく
      • 外部リソースの操作を単に包むだけのやつ
    • 本メソッドをオーバライドして、外部サービスへのアクセスをモックする
      • 【補】サービス自体のスタブを作るわけではない
  • もう一つのテスト方法: Service Stub
    • 外部サービス自体のスタブを作る
      • 【補】JSON Serverとか
    • スタブの開発作業が困難でなければうまくいく
  • GatewayのスタブとServiceのスタブを併用するのもよい
    • Gatewayのスタブ: クライアントコードのテスト用
    • Serviceのスタブ: Gatewayのテスト用
  • Pluginパターンを併用することで、本物とスタブの選択をconfiguration timeまで遅延できる

f:id:wand_ta:20190521000648p:plain
本物のGatewayを継承してテスト用Gatewayを作る(interface切ったほうが行儀いいと思う)

f:id:wand_ta:20190521000703p:plain
Serivce Stub ... サービスのスタブでGatewayをテスト


英語

  • do the trick
    • うまくいく
  • ludicrous
    • ばかばかしい
  • laudable
    • 称賛に値する