web-dev-qa-db-ja.com

サードパーティのライブラリをラップすることがベストプラクティスです

Robert C. Martinの クリーンコードブック を読んでいるときに、次のステートメントに遭遇しました。

章:7:エラー処理
ページ番号:109
..実際、サードパーティのAPIをラップすることは、ベストプラクティスです。サードパーティのAPIをラップすると、依存関係を最小限に抑えることができます。将来、大きなペナルティなしに別のライブラリに移動することを選択できます。 ラッピングを使用すると、独自のコードをテストするときに、サードパーティの呼び出しを簡単にモックアウトできます。

ラッピングの最終的な利点の1つは、特定のベンダーのAPI設計の選択に縛られないことです。使いやすいAPIを定義できます。

boldの部分についてはかなり混乱しています。

サードパーティのライブラリをラップすれば、どのようにして別のライブラリに簡単に移行できるのか、それを使ってテストを簡単にできるのか、まったく無知です。

11
CoderCroc

依存している複雑なライブラリがあるとします。ライブラリがその機能への4つの異なる呼び出しを公開し、コードベースがそれぞれを3回使用するとします。

コードで生のベンダーAPIを使用する場合、ベンダーライブラリを変更するか、ライブラリを置き換える必要がある場合は、コードベースで12のAPI呼び出しを変更する必要があります。コードとコードの間に抽象化レイヤーがある場合は、抽象化レイヤーの4か所と、コードベースのnoneをすべて変更する必要があります。これにより、労力が節約されることがわかります。労力が増えるほど、APIが提供する呼び出しが多くなります。そして、ほとんどの実際のAPIは4つ以上のエントリポイントを提供します。

19
Kilian Foth

Killian Fothの非常に良い答えに加えて、APIを使用するコードのテストに関するいくつかの問題を考慮してください。

  • APIが「実際の」何かを行うため、テストは困難です。クレジットカード決済を行うAPIを使用するコードをテストするとします。コードを実行するたびに実際のトランザクションが発生する場合、それがテストの大きな制限になります。
  • APIが返す情報を制御できないため、テストは困難です。
    • あなたのコードはクリスマスに何か特別なことをすることになっていますが、APIは現在の日付のみを返します。その機能をテストするためにクリスマスまで待ちますか?
    • 100万件のメールアドレスの処理をテストしたいのですが、これらは少数のテストユーザーしかいない外部プラットフォームからのものです。 100万人のテストユーザーを手動でそのプラットフォームに追加しますか?
    • コードは電話のGPS APIを使用して現在地を取得し、それに応じて言語を設定します。これが機能するかどうかを確認するために、実際に別の国に行く必要がありますか?

さらに多くの例を作成することができますが、あなたはアイデアを思いつきます。これらの問題はすべてAPIのラッピングによって解決されます。次に、実際のAPIを、テスト目的で必要なものを返すテストAPIに置き換えることができます。

16
user82096

あなたは疑わしいです。私はこの本を読んだことはありませんが、個人的にはこのアドバイスに強く反対します。

このアプローチは、追加のコードと追加の吸引の両方を追加します。

コードはバグとオーバーヘッドを追加する可能性があります。

抽象化には限界があり、貧しいメンテナは、ライブラリと抽象化の両方を理解して、その仕事を行う必要があります。

抽象化は難しい:基盤となるライブラリAPIをほぼ1:1で模倣するか、この時点で、この「抽象化」はもう1つではありません。または、使用しているライブラリの設計または将来使用したい将来のライブラリと確実に一致しない独自のAPIを作成します。私が現在取り組んでいるコードベースには、私たちが使用しているXMLパーサーに対するそのような抽象化があります。抽象化のAPIを満たすために、抽象化がすべてのノードのリストを保持し、何度も繰り返す必要があるという既知のパフォーマンスの問題があります。この特定のサードパーティライブラリに依存するのは10年以上前であり、変更する必要はありませんでした。このひどいアブストレーションを削除すると、ToDoリストに表示されます...

また、置き換えられるサードパーティのライブラリは例外であり、規則ではありません。このアドバイスは、起こりそうもないケースについて、前にいくつかの作業を行うように求めています。

「サードパーティlibへの模擬呼び出し」の部分については、ライブラリを模擬する必要があるケースを考えることができません。ここは少し近視眼的かもしれません。

12
barjak

私はこの慣習が悪用されることが多いと感じています。

たとえば、MongoDBクライアントを使用してMongoデータベースにアクセスするとします。すべての機能を公開したままクライアント自体をラップするのはかなり困難です。クエリオブジェクトを実装し、それを非難的なオブジェクトに変換する必要があります。その多くの作業と、後でクライアントを変更した場合、新しいオブジェクトが完全に異なるパターンに従っており、インターフェイスとラッパークラスがもはや適切ではありません。

ただし、モックされたオブジェクトなどをテストのために挿入できるようにしたいので、通常、他のものをサードパーティのライブラリに依存させることは避けます。

これは、たとえばリポジトリパターンに従って、より高いレベルでラップした場合でも達成できます。このより高いレベルでラップすることにより、サードパーティライブラリのすべての詳細に対処する必要がなくなり、ラップしている外部コードではなく、コードに関連する制限された一定のインターフェイスを公開できます。

6
Ewan

さまざまな意見が出されていますが、人々が根本的に反対しているのではないかと思います。

ポイントは、APIのラッピングcanがベストプラクティスであることです。

  1. APIのインターフェースが取るに足らないものである場合、ラップする意味がありません。インターフェースが安定していて、テストの邪魔にならず、必要以上に複雑でない場合も同じです。
  2. インターフェースが複雑でサブセットのみを使用する場合は、ラッパーを使用します。たとえば、常に同じ形式でログに記録する高度なロギングフレームワークで、アプリで使用するものはすべてLogErrorLogWarnLogInfoLogDebugです。ラッパーを使用すると、モックを使用してログメッセージをテストでき、実装を簡単に変更することもできます。
  3. 必要があるためにインターフェースが複雑で、すべてを使用すると、インターフェースを改善できず、ラッパーは元のインターフェースと同じくらい複雑になります。つまり、ラッパーは別のライブラリに移動してもメリットがありません。
3
Peter

免責事項:自分の会社のミドルウェアをラップします...

ここには複数の申し立てがあるため、個別に対処します。

また、ラッピングを使用すると、独自のコードをテストするときにサードパーティの呼び出しを簡単に模擬できます。

ここにはいくつかの利点があります。具体的にはcountingをモックする場合、呼び出しの数が役立ちます。呼び出しの数は機能しないため、テストすべきではないと主張する人もいます。ただし、呼び出しの数を10倍または100倍にした変更を導入する人を見かけました。パフォーマンスへの影響はささいなものではありません。これは特にそうです...

...一部のサードパーティライブラリには、多くの依存関係(ネットワーク接続、複雑な構成ファイル)が付属しており、テストしたい特定の状況の中でも、モックによってのみ簡単にエラーを挿入できます。

もちろん、この時点で、ラッピングは他の何よりも不要な依存関係(接続、構成ファイル)を取り除くためにさらにダウンしています。

..実際、サードパーティのAPIをラップすることがベストプラクティスです。サードパーティのAPIをラップすると、依存関係を最小限に抑えることができます。将来、大きなペナルティなしに別のライブラリに移動することを選択できます。

これは、DRY原理および抽象化と密接に関連しています。

サードパーティライブラリを呼び出してXMLドキュメントを解析し、それをナビゲートしていくつかのデータを抽出する場合、呼び出し元は

  • 使用しているサードパーティライブラリに興味がない
  • xMLドキュメントの構造におそらく興味がない

結果として、これを独自のコードにラップします。これには、抽象化レベルを上げるという二重の利点があります(XMLドキュメントではなく、キャラメルを上に置いたバニラアイスクリームを4個購入することを要求します)。

XMLドキュメントを繰り返しデコードすると、非常に迅速に自然にデコードコードの部分が何度も融合されます。これはただの乾燥です。

その結果、サードパーティのコンポーネントをラップするよりも自然に来ることがわかりました。同じ呼び出しを2回または3回使用するたびに、共通の関数でコードを抽出する傾向があり、どういうわけかラッパーになります。

(境界とビジネスモデルについての簡単な質問)

サードパーティのAPIは外国人であり、あなたの言語を話しません。

独自のコードとサードパーティのコードの間には境界があります。異なるオブジェクトを使用します。たとえば、サーバーを文字列(IPv4)と整数(ポート)としてモデル化しても、基になるAPIは文字列(IPv4:port)のみを期待するか、またはその逆です。その結果、この外部APIと通信するときに変換を適用する必要があります。

理想的には、独自のデータを処理するコードの量を最大化する必要があります。

  • あなたはそれを理解しています
  • あなたはそれを制御します(不変条件がチェックされ、アプリにとって意味があります。)
  • ...

これには、可能な限り外部コードの境界を広げる必要があります。

また、各変換には独自のミスマッチインピーダンスリスクがあるため、そのような変換が発生するコード内の場所の数を減らす必要があります。

どちらの場合も、これは、アプリケーションコードとサードパーティのAPIの間の薄層を主張しています。

2
Matthieu M.