Bir üçüncü taraf kütüphanesini sararak, üstüne ek bir soyutlama katmanı eklersiniz. Bunun birkaç avantajı var:
Kod tabanınız değişikliklere karşı daha esnek hale gelir
Kitaplığı başka bir kitapla değiştirmeniz gerekirse, uygulamanızı yalnızca paketleyicinizde - tek bir yerde değiştirmeniz gerekir . Paketleyicinin uygulamasını değiştirebilir ve başka herhangi bir şeyle ilgili bir şeyi değiştirmek zorunda kalmazsınız, başka bir deyişle, gevşek bir şekilde birleştirilmiş bir sisteme sahipsiniz. Aksi halde, kod kodunuzun tamamından geçmeniz ve her yerde değişiklikler yapmanız gerekecektir - ki bu kesinlikle istediğiniz şey değildir.
Paketleyicinin API'sini kitaplığın API'sinden bağımsız olarak tanımlayabilirsiniz.
Farklı kütüphaneler çok farklı API'lere sahip olabilir ve aynı zamanda hiçbiri tam olarak ihtiyaç duyduğunuz şey olmayabilir. Bazı kütüphanelerin her çağrıyla birlikte iletilmek için bir jetona ihtiyacı varsa? İstediğiniz yerde kütüphaneyi kullanmanız gereken yerlerde belirteci geçebilir veya daha merkezi bir yerde güvende tutabilirsiniz, ancak her durumda belirteci kullanabilirsiniz. Sarmalayıcı sınıfınız tüm bunları tekrar basitleştirir - çünkü yalnızca simgeyi sarmalayıcı sınıfınızın içinde tutabilir, uygulamanızın içindeki herhangi bir bileşene asla maruz bırakmaz ve ihtiyaçtan tamamen soyutlayabilirsiniz. İyi API tasarımını vurgulamayan bir kitaplık kullandıysanız büyük bir avantaj.
Ünite testi çok daha kolaydır
Ünite testleri sadece bir şeyi test etmelidir. Bir sınıfı sınamak istiyorsanız, bağımlılıklarını alay etmek zorundasınız. Bu sınıf, ağ çağrıları yapar veya yazılımınızın dışındaki başka bir kaynağa erişirse, bu daha da önemli hale gelir. Üçüncü taraf kütüphanesini satarak bu çağrıları alay etmek ve test verilerini ya da ünite testinin gerektirdiği şeyleri geri vermek kolaydır. Eğer böyle bir soyutlama katmanınız yoksa, bunu yapmak çok daha zor hale gelir - ve çoğu zaman bu çirkin bir kodla sonuçlanır.
Gevşek bir şekilde bağlanmış bir sistem yaratıyorsunuz
Paketleyicinizdeki değişikliklerin, yazılımınızın diğer bölümleri üzerinde hiçbir etkisi yoktur - en azından paketleyicinin davranışını değiştirmediğiniz sürece. Bu sarıcı gibi bir soyutlama katmanı ekleyerek, kitaplığa yapılan çağrıları basitleştirebilir ve uygulamanızın o kitaplığa bağımlılığını neredeyse tamamen kaldırabilirsiniz. Yazılımınız yalnızca ambalajı kullanacak ve ambalajın nasıl uygulandığına ya da ne yaptığını fark etmeyecek.
Pratik Örnek
Dürüst olalım. İnsanlar saatlerce böyle bir şeyin avantajlarını ve dezavantajlarını tartışabilir - bu yüzden size sadece bir örnek göstereyim.
Bir çeşit Android uygulamanız olduğunu ve görüntüleri indirmeniz gerektiğini varsayalım. Orada Picasso veya Universal Image Loader gibi görüntüleri yüklemeyi ve önbelleğe almayı kolaylaştıran bir kütüphane var .
Artık kullanacağımız kütüphaneyi kaydırmak için kullanacağımız bir arayüz tanımlayabiliriz:
public interface ImageService {
Bitmap load(String url);
}
Bu, görüntü yüklememiz gerektiğinde artık uygulama boyunca kullanabileceğimiz arayüz. Bu arayüzün bir uygulamasını oluşturabilir ve bu uygulamanın bir örneğini kullandığımız her yerde enjekte etmek için bağımlılık enjeksiyonunu kullanabiliriz ImageService
.
Picasso'yu kullanmaya karar verdiğimizi varsayalım. Artık ImageService
Picasso’yu dahili olarak kullanan bir uygulama yazabiliriz :
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();
}
}
Bana sorarsanız oldukça yalındır. Kütüphanelerin etrafındaki sarıcı, yararlı olması için karmaşık olmak zorunda değildir. Arayüz ve uygulama 25'den az birleştirilmiş kod satırına sahiptir, bu yüzden bunu yaratma çabası neredeyse hiç olmadı, ama zaten bunu yaparak bir şey kazandık. Context
Uygulamadaki alana bakın mu? Seçtiğiniz bağımlılık enjeksiyon çerçevesi zaten bizim bağımlılığımızı kullanmadan önce bu bağımlılığın enjekte edilmesine dikkat edecektir ImageService
, uygulamanızın şimdi resimlerin nasıl indirildiği ve ne gibi bağımlılıkların olabileceği ile ilgilenmek zorunda değildir. Uygulamanızın gördüğü bir ImageService
şeydir load()
ve bir url ile çağırdığı bir görüntüye ihtiyaç duyduğunda - basit ve anlaşılır.
Ancak asıl fayda, şeyleri değiştirmeye başladığımızda gelir. Picasso'yu Universal Image Loader ile değiştirmemiz gerektiğini hayal edin, çünkü Picasso şu anda kesinlikle ihtiyacımız olan bazı özellikleri desteklemiyor. Şimdi bizim kod tabanımızı taramak zorundayız ve ve Picasso'ya yapılan tüm çağrıları sıkıca değiştirip, bir kaç Picasso çağrısını unuttuğumuz için düzinelerce derleme hatasıyla mı uğraşmak zorundayız? Hayır. Yapmamız gereken, bundan ImageService
sonra bu uygulamayı kullanmak için yeni bir uygulama oluşturmak ve bağımlılık enjeksiyon çerçevemize söylemek:
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);
}
}
Gördüğünüz gibi uygulama çok farklı olabilir, ancak farketmez. Bizim app başka bir yerde tek bir kod satırını değiştirmek zorunda değildi. Tamamen farklı özelliklere sahip olabilecek ya da çok farklı şekilde kullanılabilecek tamamen farklı bir kütüphane kullanıyoruz ancak bizim uygulamamız umrunda değil. Bizim app geri kalanı sadece aynı yöntemiyle ImageService
arayüzü görür load()
ve bu yöntem uygulandığı sürece artık önemli değil.
En azından benim için bu zaten zaten çok güzel ses, ama bekle! Hala daha fazlası var. Üzerinde çalıştığınız bir sınıf için birim testleri yazdığınızı ve bu sınıfın kullandığını düşünün ImageService
. Elbette, birim testlerinizin başka bir sunucuda bulunan bazı kaynaklara şebeke araması yapmasına izin veremezsiniz, ancak şimdi kullandığınızdan beri, bir alay uygulayarak birim testleri için kullanılan bir statikin ImageService
kolayca load()
geri dönmesine izin verebilirsiniz :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;
}
}
Üçüncü taraf kütüphanelerini silerek özetlemek gerekirse, kod tabanınız değişikliklere karşı daha esnek hale gelir, genel olarak daha basit, test edilmesi daha kolay ve yazılımınızdaki farklı bileşenlerin eşleşmesini azaltırsınız - bir yazılımı koruduğunuzda daha da önem kazanan her şey.