見出し画像

TotT: 関心の分離(SoC)はラップしよう!

こんにちは、kubopです。
Googleにはトイレテスト(TotT)という文化があるようで、テストに関するTipsをトイレに貼り出し、テストに関する知識を全社で共有しているらしいです。
昨今はリモート勤務が広まり、TotTの実施は難しく、SlackやBotを用いてもなかなか浸透するかどうか…
そこで、noteに書きつつ自分が勉強するために、少しずつ読んで内容や、所感を書いてみようと思います。
※ 翻訳・解釈の間違いなどあるかもしれません。
その場合はこっそり教えてください。

もっとテストを書いてほしいのです。そう、あなたです。テストは、コードをリファクタリングするときや他の開発者が機能を追加するときに、 あなたを守るセーフティネットであることはもうおわかりでしょう。また、テストがコードの設計に役立つこともご存知でしょう。

私たちは、この秘密兵器(this secret weapon)を世界中の人々と共有し、私たちのテストへの情熱を広めるとともに、あなた自身やあなたの会社の他の人たちに、この重要なトリックやテクニックを楽しく簡単に学んでもらうことにしました。
このブログで定期的にエピソードを紹介し、PDFを提供しますので、プリントアウトしてご自分のバスルーム、廊下、キッチン、月面基地、秘密の地下要塞、億万長者の創業者のプリウスなど、どこにでも貼り付けてください。

Introducing "Testing on the Toilet"

Testing on the Toilet: Separation of Concerns? That's a Wrap!

次の関数は、SpeedyImgというAPIを使ってバイト配列を画像としてデコードしています。
別のチームに属するAPIを参照することにより、どのようなメンテナンス上の問題が発生するのでしょうか?

SpeedyImgImage decodeImage(List<SpeedyImgDecoder> decoders, byte[] data) {
  SpeedyImgOptions options = getDefaultConvertOptions();
  for (SpeedyImgDecoder decoder : decoders) {
    SpeedyImgResult decodeResult = decoder.decode(decoder.formatBytes(data));
    SpeedyImgImage image = decodeResult.getImage(options);
    if (validateGoodImage(image)) { return image; }
  }
  throw new RuntimeException();
}

APIの呼び出しの詳細の実装がドメインロジックと混ざり合い、コードが理解し難くなることがあります。

例えば、decoder.formatBytes()の呼び出しはAPIには必要ですが、
どのようにフォーマットされるかは、decodeImageには関係ありません。

さらに、このAPIが様々なコードで利用されている場合は、
API使用方法が変更されると、全て修正しなければなりません。
例えば、返り値をSpeedyImgResult型に変更された場合はSpeedyImgImageの利用法も変更しなければなりません。

このような保守性の問題を回避するため、ラッパー型を作成してAPIの詳細実装を抽象化の背後に隠します。

Image decodeImage(List<ImageDecoder> decoders, byte[] data) {
  for (ImageDecoder decoder : decoders) {
    Image decodedImage = decoder.decode(data);
    if (validateGoodImage(decodedImage)) { return decodedImage; }
  }
  throw new RuntimeException();
}

外部APIをラップすると、APIを呼び出すためのロジックがドメインロジックから分離されるため、関心の分離の原則(Separation of Concerns)に従います。

関心の分離(かんしんのぶんり、英語: separation of concerns、SoC)とは、ソフトウェア工学においては、プログラムを関心(何をしたいのか)毎に分離された構成要素で構築することである。

https://www.weblio.jp/content/%E9%96%A2%E5%BF%83%E3%81%AE%E5%88%86%E9%9B%A2

以下の利点があります。

  • APIの利用方法が変更された場合はAPIをラッパーでカプセル化することで、変更の影響を管理できる。

  • インターフェースや具体実装の内容は変更できるが、APIの方は変更できない。

  • 導入した型で表現できるため、別のAPIに切り替えたり追加することが簡単になる。

  • コアロジックを理解するためにAPIを読み解く必要がなくなり、可読性が向上する。

しかし、すべての外部APIをラップする必要はなく、
APIを分離するのにコストがかかる場合や、コードベースを汚さない程度に単純な場合はラッパーを導入しないほうが良い場合があります。

迷ったら、明確に改善できる場合のみラッパーを追加した方が良いです
(YAGNIの原則)

“Separation of Concerns” in the context of external APIs is also described by Martin Fowler in his blog post, Refactoring code that accesses external services.

🤔

若干コードがわかりにくかったのですが
SpeedyHogehogeを利用する代わりに汎用的なImageクラスで運用することで、

この記事が気に入ったらサポートをしてみませんか?