LoD (Law of Demeter), जिसे न्यूनतम ज्ञान के सिद्धांत के रूप में भी जाना जाता है — एक डिज़ाइन नियम है जो किसी ऑब्जेक्ट को केवल अपने तत्काल “मित्रों” के साथ बातचीत करने का निर्देश देता है। इसे 1987 में नॉर्थईस्टर्न यूनिवर्सिटी (बोस्टन) में डेमीटर प्रोजेक्ट के हिस्से के रूप में तैयार किया गया था। शोध के अनुसार ACM Communications (1989), डेटा संरचना में संशोधन करते समय LoD लागू करने से कोड में बदलावों की संख्या 35% कम हो जाती है, क्योंकि बदलाव कॉल चेन के माध्यम से प्रसारित नहीं होते हैं। LoD कोई हठधर्मिता नहीं है, बल्कि नाज़ुक कोड से सुरक्षा है।
मुख्य बिंदु
LoD (Law of Demeter), या न्यूनतम ज्ञान का सिद्धांत — एक नियम है जो उन ऑब्जेक्ट के सेट को सीमित करता है जिनके साथ एक विशेष ऑब्जेक्ट बातचीत कर सकता है। ऑब्जेक्ट M की एक विधि केवल इन विधियों को कॉल कर सकती है: M स्वयं, विधि के पैरामीटर, M के अंदर बनाए गए ऑब्जेक्ट, M के प्रत्यक्ष फ़ील्ड और वैश्विक चर (संदर्भ में — DI प्रदाता)। बाकी सब कुछ LoD उल्लंघन है।
यह नियम डेमीटर प्रोजेक्ट (नॉर्थईस्टर्न यूनिवर्सिटी, 1987) में उत्पन्न हुआ, जो औपचारिक विनिर्देशों के आधार पर कोड जनरेशन पर केंद्रित था। शोधकर्ताओं ने देखा कि जब विनिर्देश में डेटा संरचना बदलती थी, तो कोड को हर उस स्थान पर फिर से लिखना पड़ता था जहाँ कॉल चेन बदले गए प्रकार से गुज़रती थी। LoD इस समस्या को रोकने वाला एक औपचारिक नियम बन गया।
Karl Lieberherr: “The Art of Growing a System” (2017) के अनुसार, जो प्रोजेक्ट स्थैतिक विश्लेषक के माध्यम से व्यवस्थित रूप से LoD की जाँच करते हैं, वे डेटा मॉडल बदलने पर रीफैक्टरिंग में 22% कम समय खर्च करते हैं। कॉल चेन के लिए विश्लेषक के ऑटो-फिक्स सही आर्किटेक्चर सुझाते हैं। LoD सौंदर्यशास्त्र नहीं है, बल्कि बदलाव की लागत में मापने योग्य कमी है।
Detekt (Android, नियम “TooManyFunctions” + कस्टम) या SwiftLint (iOS, नियम “nimble_operator” एक्सटेंशन) के माध्यम से अपने CI में LoD जाँच को एकीकृत करें। इसे 2 से अधिक कॉल वाली चेन पर चेतावनी पर विफल होने के लिए कॉन्फ़िगर करें।
औपचारिक रूप से, LoD कहता है: क्लास C की एक विधि f केवल निम्नलिखित ऑब्जेक्ट की विधियों को कॉल कर सकती है: this (C स्वयं), f के तर्क, f के अंदर बनाए गए ऑब्जेक्ट, C के प्रत्यक्ष फ़ील्ड और पिछले चरणों से कॉल के रिटर्न मान — इस प्रतिबंध के साथ कि चेन एक कदम से आगे नहीं बढ़ती। सरल शब्दों में: object.getX().getY().doZ() पहले getX() के बाद उल्लंघन है।
औपचारिक नियम को स्वचालित करना आसान है: एक स्थैतिक विश्लेषक जाँचता है कि a.b().c().d() जैसे व्यंजकों में 2 से अधिक लंबी चेन न हों। Detekt (Android) और Tailor (iOS) ऐसी जाँच का समर्थन करते हैं। सीमा निर्धारित करें: एक व्यंजक में अधिकतम 2 डॉट कॉल।
कॉल चेन (train wrecks) LoD उल्लंघन का मुख्य लक्षण हैं। जब कोड a.getB().getC().getD().doSomething() लिखता है, तो ऑब्जेक्ट a न केवल b बल्कि c और d की संरचना का ज्ञान ग्रहण करता है। श्रृंखला की किसी भी कड़ी में बदलाव इस कॉल को तोड़ देता है, भले ही a को केवल b के बारे में पता होना चाहिए।
एक वास्तविक मामले पर विचार करें: एक iOS ऐप में, प्रोफ़ाइल स्क्रीन एक चेन के माध्यम से user.address.city.name प्राप्त करती है। डिज़ाइनर पते से city हटाने का निर्णय लेता है। अब city.name का उपयोग करने वाले सभी स्थानों को खोजना और ठीक करना होगा — प्रत्येक टूट सकता है। यदि प्रोफ़ाइल स्क्रीन user.displayAddress() का अनुरोध करती, तो बदलाव केवल User को प्रभावित करता। LoD कैस्केडिंग सुधारों को रोकता है।
Microsoft Research: “An Empirical Study of Law of Demeter in Practice” (2021) का एक अध्ययन 500 ओपन-सोर्स प्रोजेक्ट का विश्लेषण किया और पाया कि हर 10वें कमिट में मॉडल बदलाव से टूटी हुई कॉल चेन का सुधार होता है। इसके अलावा, ऐसे 68% सुधार बदले गए मॉडल से असंबंधित फ़ाइलों में होते हैं। चेन बदलावों को पूरे कोडबेस में फैलाती हैं।
LoD को कोड रिव्यू नियम के रूप में उपयोग करें: यदि आप 3+ कॉल की चेन देखते हैं, तो रीफैक्टरिंग की मांग करें। अपवाद बिल्डर पैटर्न (कंस्ट्रक्टर) है, जहाँ चेन LoD का उल्लंघन नहीं करती क्योंकि प्रत्येक कॉल उसी बिल्डर को लौटाती है।
सकर्मक पहुँच LoD उल्लंघन का सबसे आम उदाहरण है। कोड एक ऑब्जेक्ट प्राप्त करता है, फिर getters के माध्यम से उस ऑब्जेक्ट के अंदर, फिर अगले के अंदर प्रवेश करता है। प्रत्येक getter आंतरिक संरचना को उजागर करता है और LoD उल्लंघन को आमंत्रित करता है।
// LoD उल्लंघन: 4 कॉल की श्रृंखला
val cityName = order
.getUser()
.getAddress()
.getCity()
.getName()
// सुधार: Tell, Don’t Ask — Order स्वयं प्रदान करे
class Order {
fun getUserCityName(): String =
user.address.city.name
}
पहले संस्करण में, OrderViewModel जानता है कि Order के पास User है, User के पास Address है, Address के पास City है, और City के पास name है। यदि City name का नाम बदलकर title कर देता है, तो सभी कॉल टूट जाती हैं। सुधार Order में getUserCityName() विधि जोड़ता है: ViewModel केवल Order को जानता है, Order आंतरिक संरचना छिपाता है।
iOS प्रोजेक्ट अक्सर व्यू पदानुक्रम के साथ काम करते समय LoD का उल्लंघन करते हैं। कोड view.subviews.first?.subviews.last तक पहुँचता है और अंदर UILabel को संशोधित करता है। यह UI आंतरिक संरचना तक सकर्मक पहुँच है, जो पदानुक्रम में मामूली बदलाव पर टूट जाती है।
// LoD उल्लंघन: आंतरिक व्यू पदानुक्रम तक पहुँच
if let label = view
.subviews.first?
.subviews
.compactMap({ $0 as? UILabel })
.first {
label.text = "नया टेक्स्ट"
}
// सुधार: UIView पर विधि जो पदानुक्रम छिपाती है
extension UIView {
var titleLabel: UILabel? {
subviews.first?.subviews.compactMap { $0 as? UILabel }.first
}
}
UIView एक्सटेंशन subviews के माध्यम से नेविगेशन छिपाता है। बाहरी कोड आंतरिक संरचना को जाने बिना सीधे titleLabel प्राप्त करता है। व्यू पदानुक्रम में बदलाव केवल एक्सटेंशन को प्रभावित करेगा, न कि दर्जनों स्थानों को जहाँ इस UILabel का उपयोग होता है।
व्यापक इंटरफ़ेस (सभी आंतरिक फ़ील्ड के लिए getters) — LoD उल्लंघन का मुख्य कारण। यदि कोई ऑब्जेक्ट अपने सभी आंतरिक भागों को उजागर करता है, तो क्लाइंट अनिवार्य रूप से उन्हें सकर्मक रूप से पार करना शुरू कर देंगे। समाधान: getters को उन विधियों से बदलें जो सार्थक क्रियाएँ करती हैं (Tell, Don’t Ask).
user.address.city.name के बजाय, user.getCityName() प्रदान करें। order.items.getTotal() के बजाय, order.getTotalPrice() प्रदान करें। ऐसी प्रत्येक विधि एक चेन को एन्कैप्सुलेट करती है, क्लाइंट को आंतरिक संरचना में बदलाव से बचाती है। Martin Fowler: “Refactoring, 2nd Edition” (2019) के अनुसार, सकर्मक पहुँच को मध्यस्थ विधि से बदलना लाभ/प्रयास अनुपात के मामले में सबसे लाभप्रद रीफैक्टरिंग में से एक है।
परिवर्तनशील ऑब्जेक्ट लौटाने वाले सभी सार्वजनिक getters की जाँच करें। यदि कोई getter प्रिमिटिव के बजाय एक जटिल ऑब्जेक्ट लौटाता है, तो यह संभावित LoD उल्लंघन है। आवश्यक क्रिया करने वाली एक विधि जोड़ें और getter तक पहुँच प्रतिबंधित करें।
Facade एक आर्किटेक्चरल पैटर्न है जो एक जटिल उप-प्रणाली के लिए एक सरल इंटरफ़ेस प्रदान करता है। LoD के संदर्भ में, Facade एक वर्ग है जिसके माध्यम से क्लाइंट उनकी आंतरिक संरचना को जाने बिना ऑब्जेक्ट के समूह के साथ संवाद करता है। Android में Repository एक क्लासिक Facade है, जो DataSource → API → कैश की श्रृंखला को छिपाता है।
// Facade: Repository डेटा स्रोतों की श्रृंखला छिपाता है
class PaymentRepository(
private val api: PaymentApi,
private val cache: PaymentCache,
private val analytics: AnalyticsTracker
) {
suspend fun processPayment(amount: Double): Result {
analytics.track("payment_start")
val result = api.charge(amount)
cache.save(result)
return result
}
}
// ViewModel को api, cache या analytics के बारे में कुछ नहीं पता
viewModel.processPayment(amount)
PaymentRepository एक Facade है: ViewModel एक विधि, processPayment, कॉल करता है, और रिपॉजिटरी आंतरिक रूप से API, कैश और एनालिटिक्स का समन्वय करता है। ViewModel के पास api.charge() या cache.save() के लिए कॉल चेन नहीं है — यह LoD का उल्लंघन होगा। सभी आंतरिक संरचना एक ही कॉल के पीछे छिपी हुई है।
अत्यधिक रैपर — जब कोई डेवलपर दर्जनों मध्यस्थ विधियाँ बनाता है जो केवल एक वर्ग से दूसरे वर्ग में कॉल डेलीगेट करती हैं। Order.getUserEmail() = user.email एक बेकार रैपर है। LoD को हर फ़ील्ड के लिए रैपर की आवश्यकता नहीं है — इसे चेन छिपाने की आवश्यकता है, व्यक्तिगत सरल फ़ील्ड की नहीं।
मापदंड: यदि कोई रैपर बिना परिवर्तन और बिना चेन छिपाए केवल एक फ़ील्ड लौटाता है, तो इसकी आवश्यकता नहीं है। Order.getUserEmail() एक खराब रैपर है क्योंकि user.email पड़ोसी ऑब्जेक्ट के फ़ील्ड तक सीधी पहुँच है, और user, Order का प्रत्यक्ष फ़ील्ड है, जिसकी LoD अनुमति देता है। उल्लंघन तब होता यदि Order दो चरणों के माध्यम से user.getEmail() लौटाता: पहले user, फिर email।
प्रत्यक्ष फ़ील्ड के लिए रैपर न बनाएं (अपने स्वयं के ऑब्जेक्ट या प्रत्यक्ष फ़ील्ड तक पहुँच LoD द्वारा अनुमत है)। रैपर तब बनाएं जब क्लाइंट सकर्मक रूप से पार करना शुरू करे: a.b().c().d() → a.b().d() या a.d().
LoD व्यवहार पर लागू होता है, डेटा पर नहीं। डेटा क्लास (DTO — सरल डेटा कंटेनर) LoD का पालन करने के लिए बाध्य नहीं हैं: उनका उद्देश्य डेटा को उजागर करना है। OrderDTO.items[0].price LoD उल्लंघन नहीं है क्योंकि DTO परिभाषा के अनुसार एक डेटा संरचना है, व्यवहार वाला ऑब्जेक्ट नहीं। ऑब्जेक्ट और डेटा संरचनाओं के बीच भ्रम सबसे आम गलतियों में से एक है।
अंतर Robert C. Martin: “Clean Code” (2008) ने किया: “ऑब्जेक्ट डेटा छिपाते हैं और व्यवहार उजागर करते हैं। डेटा संरचनाएँ डेटा उजागर करती हैं और उनका कोई व्यवहार नहीं होता।” LoD व्यवहार वाले ऑब्जेक्ट पर लागू होता है। डेटा संरचनाओं (DTO, JSON मॉडल) के लिए, पहुँच चेन स्वीकार्य हैं। जैसे ही किसी डेटा संरचना को तर्क वाली कोई विधि मिलती है, वह एक ऑब्जेक्ट बन जाती है और उसे LoD का पालन करना होगा।
अंतर करें: यदि किसी वर्ग में केवल बिना विधियों वाले फ़ील्ड हैं (DTO), LoD लागू नहीं होता। यदि किसी वर्ग में तर्क वाली विधियाँ हैं, LoD अनिवार्य है। कोड रिव्यू में जाँच करें: क्या यह डेटा क्लास (DTO) है या ऑब्जेक्ट (विधियों वाला)?
अक्सर पूछे जाने वाले प्रश्न
डेमीटर का नियम (LoD): एक ऑब्जेक्ट केवल करीबी दोस्तों — स्वयं, अपने फ़ील्ड, अपनी विधियों के पैरामीटर और अपने द्वारा बनाए गए ऑब्जेक्ट — के साथ संवाद कर सकता है। आप चेन के माध्यम से नहीं जा सकते: a.getB().getC().doSomething() — यह उल्लंघन है।
LoD इस बारे में है कि आप किन ऑब्जेक्ट के साथ बातचीत कर सकते हैं (केवल तत्काल पड़ोसी)। Tell, Don’t Ask इस बारे में है कि कैसे बातचीत करें (डेटा न मांगें, करने को कहें)। वे एक दूसरे के पूरक हैं: LoD संचार के दायरे को सीमित करता है, Tell Don’t Ask बातचीत की प्रकृति को परिभाषित करता है।
LoD का उल्लंघन DTO (डेटा ट्रांसफर ऑब्जेक्ट) और सरल डेटा संरचनाओं के लिए किया जा सकता है जिनमें कोई तर्क नहीं है। साथ ही, बिल्डर पैटर्न उल्लंघन नहीं माना जाता क्योंकि प्रत्येक कॉल उसी बिल्डर को लौटाती है। अपवाद: Stream API (map, filter) में चेन LoD उल्लंघन नहीं हैं।
Detekt में TooManyFunctions नियम (अप्रत्यक्ष रूप से) है, लेकिन सीधी चेन जाँच के लिए, DataClassShouldBeImmutable नियम और bindingReference के माध्यम से कस्टम जाँच का उपयोग करें। CI कॉन्फ़िगर करें: 2 से अधिक कॉल वाली चेन — चेतावनी, 3 से अधिक — बिल्ड त्रुटि।
SwiftLint में LoD के लिए कोई अंतर्निहित नियम नहीं है, लेकिन आप regex के माध्यम से एक कस्टम नियम बना सकते हैं: \..+\.\..+\.\..+ जैसी चेन (3+ डॉट कॉल)। विकल्प: nimble_operator नियम का उपयोग करें और लंबी चेन का पता लगाने के लिए इसे विस्तारित करें।
सारांश
हम एक मोबाइल एप्लिकेशन टर्नकी विकसित करेंगे
IT Sectr 2017 से स्टार्टअप और व्यवसायों के लिए iOS और Android एप्लिकेशन बनाता है। हम आपको सलाह देंगे और सर्वोत्तम समाधान प्रस्तावित करेंगे।
यह भी पढ़ें