Dengan membungkus perpustakaan pihak ketiga Anda menambahkan lapisan abstraksi tambahan di atasnya. Ini memiliki beberapa keunggulan:
Basis kode Anda menjadi lebih fleksibel terhadap perubahan
Jika Anda perlu mengganti pustaka dengan pustaka lain, Anda hanya perlu mengubah implementasi Anda di pembungkus Anda - di satu tempat . Anda dapat mengubah implementasi pembungkus dan tidak perlu mengubah apa pun tentang hal lain, dengan kata lain Anda memiliki sistem yang digabungkan secara longgar. Kalau tidak, Anda harus melalui seluruh basis kode Anda dan membuat modifikasi di mana-mana - yang jelas bukan yang Anda inginkan.
Anda dapat menentukan API pembungkus secara independen dari API perpustakaan
Pustaka yang berbeda dapat memiliki API yang sangat berbeda dan pada saat yang sama tidak ada pustaka yang tepat seperti yang Anda butuhkan. Bagaimana jika beberapa perpustakaan membutuhkan token untuk diteruskan bersama dengan setiap panggilan? Anda dapat melewatkan token di aplikasi Anda di mana pun Anda perlu menggunakan perpustakaan atau Anda dapat menyimpannya di tempat yang lebih terpusat, tetapi dalam hal apa pun Anda memerlukan token tersebut. Kelas wrapper Anda membuat semuanya menjadi sederhana lagi - karena Anda bisa menyimpan token di dalam kelas wrapper Anda, jangan pernah memaparkannya ke komponen apa pun di dalam aplikasi Anda dan sepenuhnya abstrak jauh dari kebutuhan untuk itu. Keuntungan besar jika Anda pernah menggunakan perpustakaan yang tidak menekankan desain API yang baik.
Pengujian unit jauh lebih sederhana
Tes unit seharusnya hanya menguji satu hal. Jika Anda ingin menguji unit kelas Anda harus mengejek dependensinya. Ini menjadi lebih penting jika kelas itu melakukan panggilan jaringan atau mengakses sumber daya lain di luar perangkat lunak Anda. Dengan membungkus pustaka pihak ketiga, mudah untuk mengejek panggilan itu dan mengembalikan data pengujian atau apa pun yang dibutuhkan pengujian unit. Jika Anda tidak memiliki lapisan abstraksi seperti itu, menjadi jauh lebih sulit untuk melakukan ini - dan sebagian besar waktu ini menghasilkan banyak kode jelek.
Anda membuat sistem yang digabungkan secara longgar
Perubahan pada pembungkus Anda tidak berpengaruh pada bagian lain dari perangkat lunak Anda - setidaknya selama Anda tidak mengubah perilaku pembungkus Anda. Dengan memperkenalkan lapisan abstraksi seperti pembungkus ini, Anda dapat menyederhanakan panggilan ke perpustakaan dan dan hampir sepenuhnya menghapus ketergantungan aplikasi Anda di perpustakaan itu. Perangkat lunak Anda hanya akan menggunakan pembungkus dan itu tidak akan membuat perbedaan bagaimana pembungkus diimplementasikan atau bagaimana ia melakukan apa yang dilakukannya.
Contoh Praktis
Mari jujur. Orang-orang dapat berdebat tentang kelebihan dan kekurangan dari hal seperti ini selama berjam-jam - itulah sebabnya saya lebih suka hanya menunjukkan contoh kepada Anda.
Katakanlah Anda memiliki beberapa jenis aplikasi Android dan Anda perlu mengunduh gambar. Ada banyak perpustakaan di luar sana yang membuat memuat dan menyimpan gambar dengan mudah, misalnya Picasso atau Universal Image Loader .
Kita sekarang dapat mendefinisikan antarmuka yang akan kita gunakan untuk membungkus perpustakaan mana pun yang akhirnya kita gunakan:
public interface ImageService {
Bitmap load(String url);
}
Ini adalah antarmuka yang sekarang dapat kita gunakan di seluruh aplikasi kapan pun kita perlu memuat gambar. Kami dapat membuat implementasi antarmuka ini dan menggunakan injeksi ketergantungan untuk menyuntikkan contoh implementasi di mana pun kami menggunakan ImageService
.
Katakanlah kita awalnya memutuskan untuk menggunakan Picasso. Kami sekarang dapat menulis implementasi ImageService
yang menggunakan Picasso secara internal:
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();
}
}
Cukup lurus ke depan jika Anda bertanya kepada saya. Membungkus perpustakaan tidak harus rumit untuk menjadi berguna. Antarmuka dan implementasinya memiliki kurang dari 25 baris kode gabungan sehingga hampir tidak ada upaya untuk membuat ini, tapi kami sudah mendapatkan sesuatu dengan melakukan ini. Lihat Context
bidang dalam implementasinya? Kerangka kerja injeksi ketergantungan pilihan Anda sudah akan siap menyuntikkan dependensi itu sebelum kami menggunakan ImageService
aplikasi kami , sekarang aplikasi Anda tidak perlu peduli tentang bagaimana gambar diunduh dan dependensi apa pun yang mungkin dimiliki perpustakaan. Semua aplikasi Anda lihat adalah ImageService
dan ketika itu membutuhkan gambar yang dipanggil load()
dengan url - sederhana & mudah.
Namun manfaat nyata datang ketika kita mulai mengubah banyak hal. Bayangkan kita sekarang perlu mengganti Picasso dengan Universal Image Loader karena Picasso tidak mendukung beberapa fitur yang benar-benar kita butuhkan saat ini. Apakah kita sekarang harus menyisir basis kode kita dan dan dengan teliti mengganti semua panggilan ke Picasso dan kemudian menangani puluhan kesalahan kompilasi karena kita lupa beberapa panggilan Picasso? Tidak. Yang perlu kita lakukan adalah membuat implementasi baru ImageService
dan memberi tahu kerangka kerja injeksi ketergantungan kita untuk menggunakan implementasi ini mulai sekarang:
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);
}
}
Seperti yang Anda lihat implementasi mungkin sangat berbeda, tetapi itu tidak masalah. Kami tidak perlu mengubah satu baris kode pun di aplikasi kami. Kami menggunakan pustaka yang sangat berbeda yang mungkin memiliki fitur yang sama sekali berbeda atau mungkin digunakan sangat berbeda tetapi aplikasi kami tidak peduli. Sama seperti sebelumnya aplikasi kami hanya melihat ImageService
antarmuka dengan load()
metodenya dan bagaimanapun metode ini diterapkan tidak masalah lagi.
Setidaknya bagi saya ini semua sudah terdengar cukup bagus, tapi tunggu! Masih ada lagi. Bayangkan Anda sedang menulis unit test untuk kelas yang sedang Anda kerjakan dan kelas ini menggunakan ImageService
. Tentu saja Anda tidak dapat membiarkan unit test Anda melakukan panggilan jaringan ke beberapa sumber daya yang terletak di beberapa server lain, tetapi karena Anda sekarang menggunakan ImageService
Anda dapat dengan mudah membiarkan load()
kembali statis yang Bitmap
digunakan untuk unit test dengan menerapkan mocked ImageService
:
public class MockImageService implements ImageService {
private final Bitmap mMockBitmap;
public MockImageService(Bitmap mockBitmap) {
mMockBitmap = mockBitmap;
}
@Override
public Bitmap load(String url) {
return mMockBitmap;
}
}
Untuk meringkas dengan membungkus perpustakaan pihak ketiga, basis kode Anda menjadi lebih fleksibel terhadap perubahan, secara keseluruhan lebih sederhana, lebih mudah untuk diuji dan Anda mengurangi penggabungan komponen yang berbeda dalam perangkat lunak Anda - semua hal yang menjadi semakin penting saat Anda memelihara perangkat lunak.