प्रतिनिधि इंटरव्यू विषय

Kotlin इंटरव्यू: Context Parameters को स्पष्ट Dependency Injection से बेहतर कब माना जाना चाहिए?

कोडिंगकठिन
Offer.cc संपादकीय टीमप्रकाशित अपडेट किया गया

प्रश्न

Kotlin context-parameter resolution की व्याख्या करें और निर्णय लें कि यह सामान्य पैरामीटर्स या dependency-injection कंटेनर से कब बेहतर है।

प्रॉम्प्ट और संदर्भ

एक Kotlin सेवा में कई फ़ंक्शन्स को समान UserService, लॉगर, या ट्रांज़ैक्शन संदर्भ (context) की आवश्यकता होती है। टीम ग्लोबल्स में निर्भरताओं को छिपाए बिना बार-बार होने वाली पैरामीटर प्लंबिंग को कम करना चाहती है, और इसे context receivers से Kotlin 2.2 context parameters में धीरे-धीरे माइग्रेट करना होगा। सेमांटिक्स, कॉल-साइट व्यवहार, अस्पष्टता (ambiguity) से निपटने के तरीके और सीमाओं की व्याख्या करें। लक्षित दर्शक एक Kotlin/JVM इंजीनियर है; कोई DI फ्रेमवर्क पहले से मानकर नहीं चला गया है।

इंटरव्यूअर क्या मूल्यांकन करता है

एक मजबूत उत्तर यह बताता है कि context parameter एक संकलन-समय (compile-time) पर परोक्ष रूप से उपलब्ध निर्भरता है—कोई रनटाइम कंटेनर नहीं। रिज़ॉल्यूशन कॉल साइट पर वर्तमान स्कोप में एक मेल खाने वाले प्रकार (type) को ढूंढकर होता है; समान स्तर पर कई संगत मान अस्पष्ट होते हैं। उम्मीदवार context receivers और context parameters के बीच अंतर करता है, माइग्रेशन और फीचर सीमाओं को स्वीकार करता है, और बताता है कि सार्वजनिक सीमाओं पर सामान्य पैरामीटर अक्सर अधिक स्पष्ट क्यों होते हैं।

पहले पूछे जाने वाले स्पष्टीकरण

  1. क्या निर्भरता किसी सार्वजनिक मॉड्यूल या लाइब्रेरी API को पार करती है? यदि हाँ, तो स्पष्ट पैरामीटर्स को खोजना, टेस्ट करना और किसी अन्य भाषा से कॉल करना आसान होता है।
  2. क्या निर्भरता किसी स्थानीय DSL या ट्रांज़ैक्शन ऑपरेशन के अंदर स्थिर है? यदि हाँ, तो एक context parameter दोहराव वाली प्लंबिंग को हटा सकता है।
  3. क्या एक ही प्रकार के दो कार्यान्वयन (implementations) स्कोप में हो सकते हैं? यदि हाँ, तो नामित context arguments या सेमांटिक रैपर प्रकारों को डिज़ाइन करें।
  4. क्या प्रोजेक्ट पहले से context receivers का उपयोग करता है? यदि हाँ, तो कंपाइलर फ़्लैग और माइग्रेशन स्कोप की पुष्टि करें; दोनों मोड्स को एक मॉड्यूल में मिश्रित नहीं किया जा सकता है।

30-सेकंड उत्तर फ्रेमवर्क

“एक context parameter फ़ंक्शन सिग्नेचर पर एक निर्भरता घोषित करता है जबकि कॉल-साइट स्कोप इसे आपूर्ति करता है। कंपाइलर इसे प्रकार (type) द्वारा हल करता है; समान स्तर पर दो मिलान एक त्रुटि हैं। यह एक स्थानीय DSL, ट्रांज़ैक्शन या लॉगिंग संदर्भ के लिए उपयुक्त है और बार-बार होने वाली प्लंबिंग से बचाता है। सार्वजनिक APIs, क्रॉस-लैंग्वेज कॉल्स, या बार-बार बदली जाने वाली निर्भरताओं के लिए, मैं स्पष्ट पैरामीटर रखता हूँ। माइग्रेशन के दौरान मैं मिलान वाले कंपाइलर विकल्प को सक्षम करता हूँ और रिसीवर कॉल्स और क्लास-स्तरीय मामलों का निरीक्षण करता हूँ क्योंकि दोनों सुविधाएँ परस्पर विनिमेय (interchangeable) नहीं हैं।”

चरण-दर-चरण गहन विश्लेषण

निर्भरता घोषित करें, फिर इसे एक स्कोप में प्रदान करें:

kotlin
interface UserService {
  fun findUser(id: Int): String
}

context(users: UserService)
fun greeting(id: Int): String = "Hello ${users.findUser(id)}"

fun main() {
  val service = object : UserService {
    override fun findUser(id: Int) = "user-$id"
  }
  context(service) {
    println(greeting(7))
  }
}

सिग्नेचर अभी भी निर्भरता घोषित करता है, इसलिए कॉलर इसे देख सकता है; context(service) कॉल साइट पर मान की आपूर्ति करता है। रिज़ॉल्यूशन वर्तमान स्कोप में प्रकारों से मेल खाता है, वेरिएबल नामों से नहीं। यदि समान संगत प्रकार के serviceA और serviceB एक ही स्तर पर मौजूद हैं, तो संकलन चुपचाप किसी एक को चुनने के बजाय विफल हो जाता है। जब दोनों अर्थ मान्य हों तो एक स्पष्ट context argument या सेमांटिक रैपर प्रकारों का उपयोग करें।

सामान्य पैरामीटर्स के साथ सीमा सूचना घनत्व के बारे में है। यदि किसी स्थानीय DSL में एक अपरिवर्तनीय संदर्भ साझा करने वाले कई फ़ंक्शन हैं, तो एक context parameter बार-बार होने वाली प्लंबिंग को हटा देता है। यदि कोई फ़ंक्शन निर्भरता का एक बार उपयोग करता है, तो एक सामान्य पैरामीटर अधिक स्पष्ट होता है। सार्वजनिक लाइब्रेरी APIs को Java कॉल्स, IDE खोज योग्यता और जनरेट किए गए दस्तावेज़ीकरण पर भी विचार करना चाहिए; एक अंतर्निहित स्रोत जितनी दूर जाएगा, रखरखाव लागत उतनी ही अधिक होगी।

Context receivers से माइग्रेशन केवल एक कीवर्ड प्रतिस्थापन से अधिक है। JetBrains नोट करता है कि context parameters को नामों की आवश्यकता होती है और क्लास-स्तरीय context receivers का कोई सीधा समकक्ष नहीं है। एक मॉड्यूल दोनों मोड्स को सक्षम नहीं कर सकता है। प्रति मॉड्यूल कंपाइलर विकल्पों को स्विच करें, फिर कॉल साइट्स, कॉलेबल संदर्भों (callable references) और क्लास-स्तरीय रिसीवर्स का निरीक्षण करें। आधिकारिक दस्तावेज़ीकरण प्रतिबंधों को भी सूचीबद्ध करता है: कंस्ट्रक्टर्स context parameters घोषित नहीं कर सकते हैं, और संदर्भ गुणों में बैकिंग फ़ील्ड या इनिशियलाइज़र नहीं हो सकते हैं।

एक अनाम पैरामीटर एक अप्रयुक्त स्थानीय नाम से बचाता है लेकिन फिर भी रिज़ॉल्यूशन में भाग लेता है:

kotlin
context(_: UserService)
fun logGreeting(id: Int) {
  println(greeting(id))
}

निर्णय नियम है: “स्थानीय, स्थिर साझा स्थिति के लिए संदर्भ का उपयोग करें; क्रॉस-बाउंड्री निर्भरताओं, स्पष्ट प्रतिस्थापन, या एकाधिक कार्यान्वयनों के लिए सामान्य पैरामीटर या स्पष्ट DI का उपयोग करें।” Context parameters को सर्विस लोकेटर में न बदलें। टेस्ट अभी भी कॉल-साइट स्कोप के माध्यम से निर्भरताएँ प्रदान करते हैं, और एक बाहरी ऑब्जेक्ट अभी भी उनके जीवनकाल का मालिक है।

उच्च गुणवत्ता वाला नमूना उत्तर

एक context parameter एक अंतर्निहित निर्भरता की संकलन-समय घोषणा है। फ़ंक्शन अपने सिग्नेचर में UserService शामिल करता है, जबकि कॉलर context(service) के अंदर एक इंस्टेंस की आपूर्ति करता है; कंपाइलर प्रकार द्वारा वर्तमान स्कोप को खोजता है और दो मान मेल खाने पर अस्पष्टता की रिपोर्ट करता है। मैं इसे एक स्थानीय DSL, ट्रांज़ैक्शन, या लॉगिंग श्रृंखला के लिए उपयोग करूँगा जो स्थिर संदर्भ साझा करती है। सार्वजनिक APIs, Java इंटरऑप, या अक्सर बदली जाने वाली निर्भरताओं के लिए, मैं स्पष्ट पैरामीटर या DI रखूँगा। Context-receiver माइग्रेशन के दौरान, मैं प्रति मॉड्यूल कंपाइलर विकल्पों को स्विच करूँगा, नामों, कॉलेबल संदर्भों और क्लास-स्तरीय रिसीवर्स का निरीक्षण करूँगा, और कंस्ट्रक्टर्स और बैकिंग-फ़ील्ड गुणों जैसे प्रतिबंधों का ध्यान रखूँगा।

सामान्य गलतियाँ

  • गलती → context parameters को रनटाइम DI कंटेनर कहना → यह क्यों विफल होता है: कंपाइलर उन्हें कॉल साइट पर हल करता है → समाधान: स्कोप, प्रकार मिलान और संकलन-समय अस्पष्टता की व्याख्या करें।
  • गलती → यह मानना कि समान-प्रकार के मान वेरिएबल नाम से चुने जाते हैं → यह क्यों विफल होता है: समान-स्तरीय मिलान अस्पष्ट होते हैं → समाधान: एक स्पष्ट context argument या एक सेमांटिक रैपर प्रकार का उपयोग करें।
  • गलती → यांत्रिक रूप से context receiver को context parameter से बदलना → यह क्यों विफल होता है: नामकरण, क्लास-स्तरीय मामले और कॉलेबल संदर्भ भिन्न होते हैं → समाधान: प्रति मॉड्यूल माइग्रेट करें और प्रत्येक कॉल साइट का निरीक्षण करें।
  • गलती → प्रत्येक निर्भरता को अंतर्निहित बनाना → यह क्यों विफल होता है: सार्वजनिक-सीमा खोज योग्यता कम हो जाती है → समाधान: संदर्भ को स्थानीय और स्थिर रखें, और सीमाओं पर सामान्य पैरामीटर्स का उपयोग करें।

अनुवर्ती प्रश्न और उत्तर

अनुवर्ती 1: आप एक टेस्ट में संदर्भ निर्भरता को कैसे बदलते हैं?

समान इंटरफ़ेस को लागू करने वाला एक फेक या स्टब बनाएं, फिर context(fake) { ... } के अंदर विषय को चलाएं। यदि कोई टेस्ट बार-बार कार्यान्वयन बदलता है, तो स्पष्ट पैरामीटर आमतौर पर अधिक स्पष्ट होते हैं और निर्भरता को टेस्ट रिपोर्ट में दृश्यमान बनाते हैं।

अनुवर्ती 2: क्या होगा यदि समान प्रकार की दो सेवाओं को सह-अस्तित्व में होना चाहिए?

PrimaryUserService और AuditUserService जैसे सेमांटिक रैपर प्रकार पेश करें, या कॉल साइट पर एक स्पष्ट context argument पास करें। घोषणा क्रम पर भरोसा न करें क्योंकि रिज़ॉल्यूशन प्राथमिकता के रूप में क्रम का उपयोग नहीं करता है।

अनुवर्ती 3: एक कंस्ट्रक्टर context parameter घोषित क्यों नहीं कर सकता?

वर्तमान Kotlin दस्तावेज़ीकरण स्पष्ट रूप से कंस्ट्रक्टर्स को context parameters घोषित करने से रोकता है। ऑब्जेक्ट-स्तरीय निर्भरताओं के लिए, एक सामान्य कंस्ट्रक्टर पैरामीटर या स्पष्ट फैक्ट्री का उपयोग करें; एक वैश्विक संदर्भ को बाध्य करने से जीवनकाल और थ्रेड सीमाएं छिप जाएंगी।

सार्वजनिक स्रोत

संबंधित प्रश्न

संबंधित इंटरव्यू टूल

कोडिंग प्रॉम्प्ट के लिए स्क्रीनशॉट का उपयोग करें

समस्या को कैप्चर करें, फिर क्रम से प्रतिबंधों (constraints), समाधान, कोड, एज केस और जटिलता पर काम करें।

टूल देखें