サードパーティのライブラリをより大きなオブジェクトモデルでラップするための手作業を減らすにはどうすればよいですか?


16

2012年この質問の著者と2013 この質問の著者のように、アプリケーションを適切にテストするためにラップする必要があるサードパーティのライブラリがあります。一番上の答えの状態:

常にインターフェイスの背後にサードパーティのタイプとメソッドをラップする必要があります。これは退屈で痛みを伴う場合があります。コードジェネレータを記述したり、ツールを使用してこれを行うことができます。

私の場合、ライブラリはオブジェクトモデル用であり、その結果、この戦略を成功させるためにラップする必要があるクラスとメソッドの数が多くなります。単に「退屈で痛みを伴う」だけでなく、これはテストの難しい障壁になります。

この質問から4年間で、分離フレームワークが大きく進歩したことを認識しています。私の質問は、サードパーティのライブラリを完全にラップする効果を達成するためのより簡単な方法が今あるのでしょうか?このプロセスの痛みを取り除き、手作業の労力を減らすにはどうすればよいですか?


私の質問はラッピングの手間を減らすことに関するものなので、私の質問は最初にリンクした質問の複製ではありません。これらの他の質問は、ラッピングが理にかなっているかどうかを尋ねるだけであり、どのように労力を小さく保つことができるかではありません。


あなたはどんなプログラミング言語、そしてどんな種類のライブラリについて話しているのですか?
Doc Brown

@DocBrown C#およびPDF操作ライブラリ。
トム・ライト

2
あなたの質問を再開するためのサポートを見つけるために、メタに関する投稿を始めました。
ドックブラウン

@DocBrownに感謝-興味深い視点がそこにあることを期待しています。
トム・ライト

1
次の48時間でより良い回答が得られない場合、これに報奨金を置きます。
ドックブラウン

回答:


4

モックフレームワークを探していないと仮定すると、それらは超ユビキタスで見つけやすいので、事前に注目する価値のあるものがいくつかあります。

  1. 「常に」行うべき「何か」はありません。
    サードパーティのライブラリをまとめるのが常に最善とは限りません。アプリケーションが本質的にライブラリに依存している場合、または1つまたは2つのコアライブラリを中心に構築されている場合は、時間を無駄にしないでください。ライブラリが変更された場合、アプリケーションを変更する必要があります。
  2. 統合テストを使用してもかまいません。
    これは、安定しているか、アプリケーションに固有であるか、簡単にモックできない境界の周辺で特に当てはまります。これらの条件が満たされている場合、ラッピングとモック複雑で退屈です。その場合、私は両方を避けます:ラップしないでください、モックしないでください。統合テストを書くだけです。(自動テストが目標の場合。)
  3. ツールとフレームワークでは、論理的な複雑さを排除できません。
    原則として、ツールは定型文のみを削減できます。しかし、複雑なインターフェイスを取得してそれを単純にする自動化可能なアルゴリズムはありません。もちろん、インターフェイスXを取得して、ニーズに合わせ調整することはできませんこのアルゴリズムを知っているのはあなただけです!)ですから、シンラッパーを生成できるツールは間違いなくありますが、最終的にはインテリジェントに、したがって手動でコーディングする必要があるため、それらはまだユビキタスではないことをお勧めしますラッパーの後ろに隠れていても、インターフェースに対して

ただし、クラスを直接参照することを避けるために、多くの言語で使用できる戦術があります。また、場合によっては、実際には存在しないインターフェイスまたは薄いラッパーを「偽装」することもできます。たとえば、C#では、次の2つのルートのいずれかを使用します。

  1. ファクトリーと暗黙的なタイピングを使用します

この小さなコンボで複雑なクラスを完全にラップする手間を回避できます。

// "factory"
class PdfDocumentFactory {
  public static ExternalPDFLibraryDocument Build() {
    return new ExternalPDFLibraryDocument();
  }
}

// code that uses the factory.
class CoreBusinessEntity {
  public void DoImportantThings() {
    var doc = PdfDocumentFactory.Build();

    // ... i have no idea what your lib does, so, I'm making stuff but.
    // but, you can do whatever you want here without explicitly
    // referring to the library's actual types.
    doc.addHeader("Wee");
    doc.getAllText().makeBiggerBy(4).makeBold().makeItalic();
    return doc.exportBinaryStreamOrSomething();
  }
}

あなたはより多くの「機能的」アプローチを通じてまたは辞書(またはそれらを格納することにより、いずれか、メンバーとしてこれらのオブジェクトを保存しないようにすることができた場合は何でも)、このアプローチは、あなたのコアビジネスエンティティが知らなくても、コンパイル時の型チェックの利点があり、正確に作業しているクラス。

必要なのは、コンパイル時に、ファクトリによって返されるクラスに、ビジネスオブジェクトが使用しているメソッドが実際に含まれていることだけです。

  1. 動的型付けを使用します

これは暗黙的な型指定を使用するのと同じ流れですが、別のトレードオフが伴います。コンパイル型チェックを失い、外部依存関係をクラスメンバーとして匿名で追加し、依存関係を注入することができます。

class CoreBusinessEntity {
  dynamic Doc;

  public void InjectDoc(dynamic Doc) {
    Doc = doc;
  }

  public void DoImortantThings() {
    Doc.addHeader("Wee");
    Doc.getAllText().makeBiggerBy(4).makeBold().makeItalic();
    return Doc.exportBinaryStreamOrSomething();
  }
}

これらの両方の戦術では、モックするときが来るExternalPDFLibraryDocumentと、前に述べたように、やるべきことがいくつかありますが、とにかくやらなければならない仕事です。そして、この構造により、何百もの細い小さなラッパークラスを退屈に定義することを避けました。ほとんどの場合、ライブラリを直接見ることなくライブラリを使用しただけです。

以上のことから、サードパーティのライブラリを明示的にまとめることを検討する3つの大きな理由がありますがいずれもツールやフレームワークの使用を示唆するものではありません。

  1. 特定のライブラリは、アプリケーションに固有のものではありません。
  2. ラップせずにスワップするのは非常に高価です。
  3. API自体は好きではありません。

私はそれらの領域のすべての3つの内のいくつかのレベルの懸念を持っていない場合、あなたはしていない任意の作る重要なそれを包むための努力を。また、3つの領域すべてに何らかの懸念がある場合、自動生成されたシンラッパーは実際には役立ちません。

ライブラリをラップすることにした場合、時間を最も効率的かつ効果的に使用するのは、必要なインターフェイスに対してアプリケーション構築することです既存のAPIに対してではありません。

別の言い方をすれば、古典的なアドバイスに注意してください。可能な限りの決定をすべて延期します。最初にアプリケーションの「コア」をビルドします。最終的に希望することを実行するインターフェースに対するコード。これは、最終的にはまだ存在しない「周辺機器」によって実現されます。必要に応じてギャップを埋めます。

この努力はないかもしれないと感じた時間、保存されたように、しかし、ラッパーが必要だと思う場合、これは安全にそれを行う最も効率的な方法です。

このように考えてください。

たとえコードがラップされていても、コードの暗いコーナーでこのライブラリに対してコードを作成する必要がありますテスト中にライブラリをモックすると、たとえそれがラップアップされていても、避けられない手作業がそこにあります。しかし、それは、アプリケーションの大部分でそのライブラリを名前で直接確認する必要があるという意味ではありません。

TLDR

ライブラリをラッピングする価値がある場合は、戦術を使用して、サードパーティライブラリへの広範な直接参照を回避しますが、シンラッパーを生成するためのショートカットを使用しないでください。最初にビジネスロジックを構築し、インターフェイスについて慎重に検討し、必要に応じてアダプタを有機的に出現させます

そして、それに関しては、統合テストを恐れないでください。それらは少しファジーですが、それでも動作コードの証拠を提供し、回帰を寄せ付けないように簡単に作成できます。


2
明示的だった-これは質問応答ではない別のポストではない「手動労力を削減する方法」「ラップするときに」、しかし。
ドックブラウン

1
申し訳ありませんが、質問の核心を逃したと思います。私はあなたの提案がどの減らすことができる方法を見ていないマニュアルラッピングに努力を。利害関係のlibには、APIとして複雑なオブジェクトモデルがあり、数十個のクラスと数百個のメソッドがあるとします。そのAPIは標準的な使用方法の場合と同様に問題ありませんが、痛みや労力を減らして単体テスト用にラップする方法はありますか?
ドックブラウン

1
TLDR; ボーナスポイントが必要な場合は、まだわからないことを教えてください;-)
Doc Brown

1
@DocBrownポイントを見逃しませんでした。しかし、あなたは私の答えのポイント逃したと思います適切な努力を投資することで、多くの作業を節約できます。テストの意味がありますが、それは単なる副作用です。-自動的にツールを使用すると、ライブラリはまだ葉薄いラッパあなたが他の誰かのAPIの周りにあなたのコアライブラリを構築し、モックを構築生成される多くの労力ができないライブラリーアウトを出る時に、あなたしているしつこい場合は避けることをテストの。
svidgen 16

1
@DocBrownこれはばかげています。「このクラスの問題には、ツールまたはアプローチのクラスがありますか?」はい。もちろんそうです。防御的なコーディングと、単体テストについて独断的にならないこと ...私の答えが言っているように。自動的に生成されるシンラッパーがある場合、それどのような価値をもたらしますか?...テストのために依存関係を挿入することはできませんが、それでも手動で行う必要があります。そして、あなたはまだライブラリのAPIに対してコーディングしているので、あなたはライブラリを交換することはできません...それはただ間接的です。
svidgen 16

9

そのコードを単体テストしないでください。代わりに統合テストを作成してください。場合によっては、ユニットテスト(モック)退屈で苦痛です。単体テストを捨てて、実際にベンダーに呼びかける統合テストを作成します。

これらのテストは、展開後、自動化されたアクティビティとして展開後に実行する必要があります。これらは、単体テストの一部としても、ビルドプロセスの一部としても実行されません。

統合テストは、アプリケーションの特定の部分の単体テストよりも適している場合があります。コードを「テスト可能」にするために通過しなければならないフープは、時には有害な場合があります。


2
あなたの答えを読んだときに私が最初に思ったのは、「バイナリレベルでファイルを比較しても変更点や変更に問題があるかどうかがわからないため、PDFにとって非現実的」でした。次に、この古いSO投稿を見つけました。実際に機能する可能性があることを示しています(+1)。
Doc Brown

SOリンクのツール@DocBrownは、バイナリレベルのPDFを比較しません。ページの構造とコンテンツを比較します。:PDF内部構造safaribooksonline.com/library/view/pdf-explained/9781449321581/...
linuxunil

1
@linuxunil:はい、私が書いたように、私は最初に考えました...しかし、その後、私は上記の解決策を見つけました。
ドックブラウン

ああ..なるほど..あなたの答えの最後の「実際にうまくいくかもしれない」と私は混乱しました。
linuxunil 16

1

私が理解しているように、この議論はラッパー作成自動化の機会に焦点を当てていますアイデアと実装のガイドラインをまとめるのではなく、ます。ここにはすでに多くのことがあるので、私はこのアイデアから抽象化しようとします。

私たちは.NETテクノロジーをいじっているので、強力なリフレクション機能を手にしています。あなたが考慮することがあります:

  1. .NET Wrapper Class Generatorのようなツール。私はそのツールを使用したことがなく、レガシーテクノロジースタック上で動作することを知っていますが、おそらくあなたの場合には適合するでしょう。もちろん、コードの品質、依存関係の反転、およびインターフェイスの分離のサポートについては、個別に調査する必要があります。そのようなツールは他にもあるかもしれませんが、あまり検索しませんでした。
  2. 参照されたアセンブリでパブリックメソッド/インターフェイスを検索し、マッピングとコード生成を行う独自のツールを作成します。コミュニティへの貢献は大歓迎です!
  3. .NETが当てはまらない場合は、こちらをご覧ください

このような自動化は、最終的な解決策ではなく、ベースポイントを構成する可能性があると思います。手動でコードのリファクタリングが必要になります...または最悪の場合、svidgenが書いたことに完全に同意するため、再設計が必要です:

必要なインターフェイスに対してアプリケーションを構築します。サードパーティAPIに対してではありません。


OK、少なくとも問題どのように処理されるかについてのアイデア。しかし、このユースケースの自身の経験に基づいているのではないでしょうか?
ドックブラウン

質問は、ソフトウェアエンジニアリングドメイン(ラッパー、リフレクション、ディ)に関する私自身の経験に基づいています!ツールについて-私はasnwerで述べられているようにそれを使用しませんでした。
tom3k 16

0

ラッパーライブラリを作成するときは、次のガイドラインに従ってください。

  • 現在必要なサードパーティライブラリの小さなサブセットのみを公開します(およびオンデマンドで拡張します)
  • ラッパーはできるだけ薄くしてください(そこにロジックはありません)。

1
質問の要点を逃した投稿に対してなぜ3つのアップ投票を得たのか疑問に思うだけです。
ドックブラウン
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.