थर्ड पार्टी लाइब्रेरी को रैप करके आप उसके ऊपर एब्सट्रैक्शन की एक अतिरिक्त परत जोड़ते हैं। इसके कुछ फायदे हैं:
आपका कोड आधार परिवर्तनों के प्रति अधिक लचीला हो जाता है
यदि आपको कभी भी पुस्तकालय को किसी अन्य के साथ बदलने की आवश्यकता है, तो आपको केवल अपने आवरण में अपने कार्यान्वयन को बदलने की आवश्यकता है - एक ही स्थान पर । आप आवरण के कार्यान्वयन को बदल सकते हैं और किसी भी चीज़ के बारे में कोई चीज़ नहीं बदलनी चाहिए, दूसरे शब्दों में आपके पास एक शिथिल युग्मित प्रणाली है। अन्यथा आपको अपने पूरे कोडबेस के माध्यम से जाना होगा और हर जगह संशोधन करना होगा - जो स्पष्ट रूप से वह नहीं है जो आप चाहते हैं।
आप लाइब्रेरी के एपीआई से स्वतंत्र रूप से रैपर के एपीआई को परिभाषित कर सकते हैं
विभिन्न पुस्तकालयों में अलग-अलग एपीआई हो सकते हैं और एक ही समय में उनमें से कोई भी बिल्कुल वैसा ही हो सकता है जैसा आपको चाहिए। क्या होगा अगर हर लाइब्रेरी को हर कॉल के साथ पास करने के लिए टोकन चाहिए? आप अपने ऐप में जहां भी आपको लाइब्रेरी का उपयोग करने की आवश्यकता होती है, उसके चारों ओर एक टोकन पास कर सकते हैं या आप इसे केंद्र में कहीं और सुरक्षित कर सकते हैं, लेकिन किसी भी स्थिति में आपको टोकन की आवश्यकता होती है। आपका रैपर वर्ग इस पूरी चीज़ को फिर से सरल बनाता है - क्योंकि आप केवल अपने रैपर क्लास के अंदर टोकन रख सकते हैं, इसे कभी भी अपने ऐप के अंदर किसी भी घटक के लिए उजागर न करें और इसकी आवश्यकता पूरी तरह से दूर कर दें। एक बड़ा फायदा अगर आपने कभी एक पुस्तकालय का उपयोग किया है जो अच्छे एपीआई डिजाइन पर जोर नहीं देता है।
इकाई परीक्षण तरीका सरल है
यूनिट परीक्षण केवल एक चीज का परीक्षण करना चाहिए। यदि आप एक वर्ग का परीक्षण करना चाहते हैं, तो आपको इसकी निर्भरता का मजाक उड़ाना होगा। यह और भी महत्वपूर्ण हो जाता है यदि वह वर्ग नेटवर्क कॉल करता है या आपके सॉफ़्टवेयर के बाहर किसी अन्य संसाधन को एक्सेस करता है। तीसरे पक्ष के पुस्तकालय को लपेटकर उन कॉलों को मॉक करना और परीक्षण डेटा या उस इकाई परीक्षण के लिए जो भी आवश्यक हो, वापस करना आसान है। यदि आपके पास अमूर्तता की ऐसी परत नहीं है, तो ऐसा करना अधिक कठिन हो जाता है - और अधिकांश समय यह बहुत अधिक बदसूरत कोड होता है।
आप एक शिथिल युग्मित प्रणाली बनाते हैं
आपके आवरण के परिवर्तनों का आपके सॉफ़्टवेयर के अन्य हिस्सों पर कोई प्रभाव नहीं पड़ता है - कम से कम जब तक आप अपने आवरण के व्यवहार को नहीं बदलते हैं। इस आवरण की तरह अमूर्तता की एक परत को पेश करके आप लाइब्रेरी को कॉल को सरल बना सकते हैं और उस लाइब्रेरी पर अपने ऐप की निर्भरता को लगभग पूरी तरह से हटा सकते हैं। आपका सॉफ़्टवेयर बस आवरण का उपयोग करेगा और इससे कोई फर्क नहीं पड़ेगा कि आवरण कैसे कार्यान्वित होता है या यह कैसे करता है।
व्यावहारिक उदाहरण
चलो ईमानदार बनें। लोग घंटों तक कुछ इस तरह के फायदे और नुकसान के बारे में बहस कर सकते हैं - यही वजह है कि मैं बहुत कुछ सिर्फ आपको एक उदाहरण दिखाता हूं।
मान लीजिए कि आपके पास किसी प्रकार का Android एप्लिकेशन है और आपको चित्र डाउनलोड करने की आवश्यकता है। वहाँ पुस्तकालयों का एक समूह है जो लोडिंग और कैशिंग छवियों को उदाहरण के लिए पिकासो या यूनिवर्सल इमेज लोडर के लिए एक हवा बनाते हैं ।
अब हम एक इंटरफ़ेस को परिभाषित कर सकते हैं जिसका उपयोग हम करने जा रहे हैं जो भी पुस्तकालय का उपयोग करते हुए समाप्त होता है:
public interface ImageService {
Bitmap load(String url);
}
यह वह इंटरफ़ेस है जिसे हम अब पूरे ऐप में उपयोग कर सकते हैं जब भी हमें किसी इमेज को लोड करने की आवश्यकता होती है। हम इस इंटरफ़ेस का कार्यान्वयन बना सकते हैं और उस कार्यान्वयन की एक आवृत्ति को इंजेक्ट करने के लिए निर्भरता इंजेक्शन का उपयोग कर सकते हैं जहाँ हम उपयोग करते हैं ImageService
।
मान लीजिए कि हम शुरू में पिकासो का उपयोग करने का निर्णय लेते हैं। अब हम एक कार्यान्वयन लिख सकते हैं ImageService
जिसके लिए पिकासो आंतरिक रूप से उपयोग करता है:
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()
एक यूआरएल के साथ कॉल करता है - सरल और सीधा।
हालाँकि असली लाभ तब होता है जब हम चीजों को बदलना शुरू करते हैं। कल्पना कीजिए कि अब हमें पिकासो को यूनिवर्सल इमेज लोडर से बदलने की आवश्यकता है क्योंकि पिकासो कुछ ऐसी सुविधा का समर्थन नहीं करता है जिसकी हमें अभी आवश्यकता है। क्या अब हमें अपने कोडबेस के माध्यम से कंघी करनी है और पिकासो को सभी कॉलों को बदलने की आवश्यकता है और फिर दर्जनों संकलन त्रुटियों से निपटें क्योंकि हम कुछ पिकासो कॉल को भूल गए? नहीं, हमें बस इतना करना है 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);
}
}
जैसा कि आप देख सकते हैं कि कार्यान्वयन बहुत भिन्न हो सकता है, लेकिन इससे कोई फर्क नहीं पड़ता। हमें अपने ऐप में कहीं और कोड की एक भी लाइन नहीं बदलनी थी। हम एक पूरी तरह से अलग लाइब्रेरी का उपयोग करते हैं, जिसमें पूरी तरह से अलग विशेषताएं हो सकती हैं या बहुत अलग तरीके से उपयोग की जा सकती हैं, लेकिन हमारा ऐप बस परवाह नहीं करता है। हमारे ऐप के बाकी हिस्सों से पहले के समान ही 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;
}
}
तीसरे पक्ष के पुस्तकालयों को लपेटकर सारांशित करने के लिए आपका कोड आधार परिवर्तन के लिए अधिक लचीला हो जाता है, समग्र सरल, परीक्षण करना आसान होता है और आप अपने सॉफ़्टवेयर में विभिन्न घटकों के युग्मन को कम कर देते हैं - सभी चीजें जो एक सॉफ्टवेयर को बनाए रखने के लिए अधिक से अधिक महत्वपूर्ण हो जाती हैं।