サードパーティのライブラリをラップすることにより、その上に抽象化のレイヤーを追加します。これにはいくつかの利点があります。
コードベースは変更に対してより柔軟になります
ライブラリを別のライブラリに置き換える必要がある場合は、ラッパーの実装を1か所で変更するだけです。ラッパーの実装を変更できます。他のことを変更する必要はありません。つまり、疎結合システムを使用しています。それ以外の場合は、コードベース全体を調べて、どこでも変更を行う必要があります。これは明らかに必要なものではありません。
ライブラリのAPIとは別にラッパーのAPIを定義できます
異なるライブラリは、非常に異なるAPIを持つことができますが、同時に、それらのどれもあなたが必要とするものではないかもしれません。一部のライブラリで、すべての呼び出しとともにトークンを渡す必要がある場合はどうなりますか?ライブラリを使用する必要がある場所ならどこでもトークンをアプリ内で渡すことができます。または、より中央のどこかに安全に保管できますが、いずれにしてもトークンが必要です。ラッパークラスにより、この全体が再び単純になります。トークンをラッパークラス内に保持するだけで、アプリ内のコンポーネントにトークンを公開せずに、その必要性を完全に抽象化できるためです。優れたAPIデザインを強調していないライブラリを使用したことがある場合、大きな利点があります。
単体テストはずっと簡単です
単体テストは1つのことのみをテストする必要があります。クラスを単体テストする場合は、その依存関係を模擬する必要があります。そのクラスがネットワーク呼び出しを行うか、ソフトウェアの外部の他のリソースにアクセスする場合、これはさらに重要になります。サードパーティのライブラリをラップすることにより、これらの呼び出しをモックし、テストデータまたは単体テストに必要なものを簡単に返すことができます。このような抽象化レイヤーがない場合、これを行うのははるかに困難になります。ほとんどの場合、これは多くのlotいコードになります。
疎結合システムを作成します
ラッパーの変更は、少なくともラッパーの動作を変更しない限り、ソフトウェアの他の部分には影響しません。このラッパーのような抽象化レイヤーを導入することにより、ライブラリーへの呼び出しを簡素化し、そのライブラリーへのアプリの依存関係をほぼ完全に削除できます。あなたのソフトウェアはラッパーを使用するだけで、ラッパーの実装方法や実行方法に違いはありません。
実用例
正直になりましょう。人々はこのようなものの長所と短所について何時間も議論することができます-それが私がむしろあなたに例を示している理由です。
ある種のAndroidアプリがあり、画像をダウンロードする必要があるとします。PicassoやUniversal Image Loaderなど、画像の読み込みとキャッシュを簡単にするライブラリがたくさんあります。
これで、使用するライブラリをラップするために使用するインターフェイスを定義できます。
public interface ImageService {
Bitmap load(String url);
}
これは、画像を読み込む必要があるときにアプリ全体で使用できるインターフェイスです。このインターフェイスの実装を作成し、依存関係の注入を使用して、を使用するすべての場所にその実装のインスタンスを注入できますImageService
。
最初にピカソを使用することにしたとしましょう。ImageService
これで、Picassoを内部で使用する実装を作成できます。
public class PicassoImageService implements ImageService {
private final Context mContext;
public PicassoImageService(Context context) {
mContext = context;
}
@Override
public Bitmap load(String url) {
return Picasso.with(mContext).load(url).get();
}
}
あなたが私に尋ねると、かなり簡単です。ライブラリを包むラッパーは、便利にするために複雑にする必要はありません。インターフェイスと実装のコードの合計行数は25未満であるため、これを作成することはほとんどありませんでしたが、すでにこれを行うことで何かを得ています。Context
実装のフィールドを参照してください?選択した依存性注入フレームワークは、を使用する前にその依存性の注入をすでに処理しているためImageService
、アプリは画像のダウンロード方法やライブラリが持つ依存性を気にする必要がなくなりました。アプリにImageService
表示されるのはload()
であり、URLを使用して呼び出す画像が必要な場合-シンプルで簡単です。
しかし、物事を変え始めると、本当のメリットが得られます。Picassoは現在必要な機能をサポートしていないため、PicassoをUniversal Image Loaderに置き換える必要があると想像してください。コードベースをくまなく調べて、ピカソへのすべての呼び出しを退屈に置き換え、ピカソへの呼び出しをいくつか忘れていたため、数十のコンパイルエラーに対処する必要がありますか?いいえ。必要なのは、新しい実装を作成し、ImageService
依存性注入フレームワークに今後この実装を使用するように指示することだけです。
public class UniversalImageLoaderImageService implements ImageService {
private final ImageLoader mImageLoader;
public UniversalImageLoaderImageService(Context context) {
DisplayImageOptions defaultOptions = new DisplayImageOptions.Builder()
.cacheInMemory(true)
.cacheOnDisk(true)
.build();
ImageLoaderConfiguration config = new ImageLoaderConfiguration.Builder(context)
.defaultDisplayImageOptions(defaultOptions)
.build();
mImageLoader = ImageLoader.getInstance();
mImageLoader.init(config);
}
@Override
public Bitmap load(String url) {
return mImageLoader.loadImageSync(url);
}
}
ご覧のとおり、実装は大きく異なる可能性がありますが、重要ではありません。アプリの他の場所でコードを1行も変更する必要はありませんでした。完全に異なる機能を持つか、まったく異なる方法で使用される可能性のある完全に異なるライブラリを使用しますが、アプリは気にしません。アプリの残りの部分がImageService
そのload()
メソッドを備えたインターフェイスを見るだけで、このメソッドが実装されることは重要ではありません。
少なくとも私にとっては、これはすでにかなりいい音に聞こえますが、待ってください!まだまだあります。作業中のクラスの単体テストを作成しており、このクラスがを使用しているとしImageService
ます。もちろん、単体テストで他のサーバーにあるリソースへのネットワーク呼び出しを行うことはできませんが、現在使用しているので、模擬テストを実装するImageService
ことで簡単に単体テストに使用されるload()
静的を返すことができます:Bitmap
ImageService
public class MockImageService implements ImageService {
private final Bitmap mMockBitmap;
public MockImageService(Bitmap mockBitmap) {
mMockBitmap = mockBitmap;
}
@Override
public Bitmap load(String url) {
return mMockBitmap;
}
}
サードパーティのライブラリをラップすることで要約すると、コードベースは変更に対してより柔軟になり、全体的にシンプルで、テストが容易になり、ソフトウェア内のさまざまなコンポーネントの結合を減らします-ソフトウェアを長期間維持するほどますます重要になるすべてのもの。