मौलिक रूप से, प्रतिबिंब का अर्थ है डेटा के रूप में अपने प्रोग्राम के कोड का उपयोग करना।
इसलिए, प्रतिबिंब का उपयोग करना एक अच्छा विचार हो सकता है जब आपके प्रोग्राम का कोड डेटा का एक उपयोगी स्रोत है। (लेकिन ट्रेड-ऑफ हैं, इसलिए यह हमेशा एक अच्छा विचार नहीं हो सकता है।)
उदाहरण के लिए, एक साधारण वर्ग पर विचार करें:
public class Foo {
public int value;
public string anotherValue;
}
और आप इससे XML उत्पन्न करना चाहते हैं। आप XML बनाने के लिए कोड लिख सकते हैं:
public XmlNode generateXml(Foo foo) {
XmlElement root = new XmlElement("Foo");
XmlElement valueElement = new XmlElement("value");
valueElement.add(new XmlText(Integer.toString(foo.value)));
root.add(valueElement);
XmlElement anotherValueElement = new XmlElement("anotherValue");
anotherValueElement.add(new XmlText(foo.anotherValue));
root.add(anotherValueElement);
return root;
}
लेकिन यह बहुत सारे बॉयलरप्लेट कोड है, और हर बार जब आप क्लास बदलते हैं, तो आपको कोड अपडेट करना होगा। वास्तव में, आप वर्णन कर सकते हैं कि यह कोड क्या करता है
- वर्ग के नाम के साथ एक XML तत्व बनाएँ
- वर्ग की प्रत्येक संपत्ति के लिए
- संपत्ति के नाम के साथ एक XML तत्व बनाएं
- संपत्ति का मूल्य XML तत्व में डालें
- XML तत्व को रूट में जोड़ें
यह एक एल्गोरिथ्म है, और एल्गोरिथ्म का इनपुट वर्ग है: हमें इसके नाम, और इसके गुणों के नाम, प्रकार और मूल्यों की आवश्यकता है। यह वह जगह है जहां प्रतिबिंब आता है: यह आपको इस जानकारी तक पहुंच प्रदान करता है। जावा आपको Class
कक्षा के तरीकों का उपयोग करके प्रकारों का निरीक्षण करने की अनुमति देता है ।
कुछ और उपयोग के मामले:
- एक वेबसर्वर में URL को एक क्लास के मेथड नेम, और URL के मेथड तर्कों के आधार पर परिभाषित करते हैं
- एक वर्ग की संरचना को एक ग्राफ़िकल टाइप परिभाषा में परिवर्तित करें
- एक वर्ग के हर तरीके को कॉल करें जिसका नाम यूनिट टेस्ट केस के रूप में "टेस्ट" से शुरू होता है
हालांकि, पूर्ण प्रतिबिंब का मतलब न केवल मौजूदा कोड को देखना है (जो अपने आप में "आत्मनिरीक्षण" के रूप में जाना जाता है), बल्कि कोड को संशोधित या संशोधित भी कर सकता है। इसके लिए जावा में दो प्रमुख उपयोग के मामले हैं: प्रॉक्सी और मोक्स।
मान लीजिए कि आपके पास एक इंटरफ़ेस है:
public interface Froobnicator {
void froobnicateFruits(List<Fruit> fruits);
void froobnicateFuel(Fuel fuel);
// lots of other things to froobnicate
}
और आपके पास एक कार्यान्वयन है जो कुछ दिलचस्प करता है:
public class PowerFroobnicator implements Froobnicator {
// awesome implementations
}
और वास्तव में आपके पास एक दूसरा कार्यान्वयन भी है:
public class EnergySaverFroobnicator implements Froobnicator {
// efficient implementations
}
अब आप कुछ लॉग आउटपुट भी चाहते हैं; जब भी कोई विधि कहा जाता है आप बस एक लॉग संदेश चाहते हैं। आप स्पष्ट रूप से हर विधि में लॉग आउटपुट जोड़ सकते हैं, लेकिन यह कष्टप्रद होगा, और आपको इसे दो बार करना होगा; प्रत्येक कार्यान्वयन के लिए एक बार। (इसलिए और भी जब आप अधिक कार्यान्वयन जोड़ते हैं।)
इसके बजाय, आप एक प्रॉक्सी लिख सकते हैं:
public class LoggingFroobnicator implements Froobnicator {
private Logger logger;
private Froobnicator inner;
// constructor that sets those two
public void froobnicateFruits(List<Fruit> fruits) {
logger.logDebug("froobnicateFruits called");
inner.froobnicateFruits(fruits);
}
public void froobnicateFuel(Fuel fuel) {
logger.logDebug("froobnicateFuel( called");
inner.froobnicateFuel(fuel);
}
// lots of other things to froobnicate
}
फिर, हालांकि, एक दोहरावदार पैटर्न है जिसे एक एल्गोरिथ्म द्वारा वर्णित किया जा सकता है:
- एक लकड़हारा प्रॉक्सी एक वर्ग है जो एक इंटरफ़ेस लागू करता है
- इसमें एक कंस्ट्रक्टर है जो इंटरफ़ेस का एक और कार्यान्वयन और एक लकड़हारा लेता है
- इंटरफ़ेस में हर विधि के लिए
- कार्यान्वयन एक संदेश "$ methodname" कहा जाता है
- और फिर सभी तर्कों के साथ गुजरते हुए, आंतरिक इंटरफ़ेस पर समान विधि को कॉल करता है
और इस एल्गोरिथ्म का इनपुट इंटरफ़ेस परिभाषा है।
प्रतिबिंब आपको इस एल्गोरिथ्म का उपयोग करके एक नए वर्ग को परिभाषित करने की अनुमति देता है। जावा आपको java.lang.reflect.Proxy
कक्षा के तरीकों का उपयोग करके ऐसा करने की अनुमति देता है , और ऐसे पुस्तकालय हैं जो आपको और भी अधिक शक्ति प्रदान करते हैं।
तो प्रतिबिंब के डाउनसाइड क्या हैं?
- आपका कोड समझना कठिन हो जाता है। आप अपने कोड के ठोस प्रभावों से हटाए गए अमूर्त स्तर का एक स्तर हैं।
- आपका कोड डीबग करना कठिन हो जाता है। विशेष रूप से कोड-जनरेटिंग लाइब्रेरी के साथ, निष्पादित कोड वह कोड नहीं हो सकता है जिसे आपने लिखा था, लेकिन आपके द्वारा उत्पन्न कोड, और डीबगर आपको उस कोड को दिखाने में सक्षम नहीं हो सकता है (या आपको ब्रेकपॉइंट लगाने देता है)।
- आपका कोड धीमा हो जाता है। हार्ड-कोडिंग एक्सेस के बजाय गतिशील रूप से पढ़ने की जानकारी और उनके रनटाइम हैंडल द्वारा खेतों तक पहुंच धीमी है। डिबग करने के लिए और भी कठिन होने की कीमत पर डायनामिक कोड जेनरेशन इस प्रभाव को कम कर सकता है।
- आपका कोड अधिक नाजुक हो सकता है। डायनेमिक रिफ्लेक्शन एक्सेस कंपाइलर द्वारा टाइप-चेक नहीं किया जाता है, बल्कि रनटाइम में एरर्स को फेंकता है।