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» کی توسیع) کے ذریعے LoD کی جانچ اپنے CI میں ضم کریں۔ اسے 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.name استعمال کرنے والی تمام جگہوں کو چہانے اور ٹھیک کرنے کی ضرورت ہے — ہر ایک ٹوٹ سکتی ہے۔ اگر پروفائل سکرین user.displayAddress() کی درخواست کرتی، تبدیلی صرف User کو متاثر کرتی۔ LoD یک دوسرے کے بعد ایک دوسرے تصحیح کو روکتا ہے۔
Microsoft Research: «An Empirical Study of Law of Demeter in Practice» (2021) کے ایک مطالعے نے 500 آپن سورس مناصبوں کا تجزیہ کیا اور پایا کہ ہر 10 ویں کمٹ میں ایک ماڈل تبدیلی سے ٹوٹی گئی کال چین کی درستی موجود ہوتی ہے۔ مزید برھان، ایسی 68 فیصد درستیاں ان فائلوں میں ہیں جو بدلے گئے ماڈل سے متعلق نہیں ہیں۔ زنجیریں تبدیلیوں کو پورے کوڈ بیس میں پھیلاتی ہیں۔
LoD کو کوڈ جائزہ کے قاعدے کے طور پر استعمال کریں: اگر آپ 3+ کالز کی زنجیر دیکھتے ہیں، ری فیکٹرنگ کا مطالبہ کریں۔ مستشنا Builder پیٹرن (کنسٹرکٹر) ہے، جہاں زنجیر LoD کی خلاف ورزی نہیں کرتی کیونکہ ہر کال وہی builder واپس کرتا ہے۔
متعدی رسائی 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 رویہ پر لاگو ہوتا ہے، ڈیٹا پر نہیں۔ ڈیٹا کلاسز (DTOs — سادہ ڈیٹا کنٹینر) LoD پر عمل کرنے کے پابند نہیں ہیں: ان کا مقصد ڈیٹا کو ظاہر کرنا ہے۔ OrderDTO.items[0].price LoD خلاف ورزی نہیں ہے کیونکہ DTO تعریف کے مطابق ایک ڈیٹا سٹرکچر ہے، رویہ والا آبجیکٹ نہیں۔ آبجیکٹس اور ڈیٹا سٹرکچرز کے درمیان خلط بہت سب سے عام غلطیوں میں سے ایک ہے۔
یہ تمیز Robert C. Martin: «Clean Code» (2008) نے کی: «آبجیکٹس ڈیٹا چھپاتے ہیں اور رویہ ظاہر کرتے ہیں۔ ڈیٹا سٹرکچرز ڈیٹا ظاہر کرتے ہیں اور ان کا کوئی رویہ نہیں ہوتا۔» LoD رویہ والے آبجیکٹس پر لاگو ہوتا ہے۔ ڈیٹا سٹرکچرز (DTOs، JSON ماڈلز) کے لیے، رسائی کی زنجیریں جائز ہیں۔ جیسے ہی ایک ڈیٹا سٹرکچر منطق والا ایک طریقہ حاصل کرتا ہے، یہ ایک آبجیکٹ بن جاتا ہے اور LoD پر عمل کرنا ضروری ہو جاتا ہے۔
فرق کریں: اگر ایک کلاس میں طریقوں کے بغیر صرف فیلڈز ہیں (DTO)، تو LoD لاگو نہیں ہوتا۔ اگر ایک کلاس میں منطق والے طریقے ہیں، تو LoD لازمی ہے۔ کوڈ جائزے میں چیک کریں: یہ ڈیٹا کلاس (DTO) ہے یا آبجیکٹ (طریقوں کے ساتھ)؟
اکثر پوچھے جانے والے سوالات
ڈیمیٹر کا قانون (LoD): ایک آبجیکٹ صرف قریبی دوستوں — خود، اپنے فیلڈز، اپنے طریقوں کے پیرامیٹرز اور خود کے بنائے گئے آبجیکٹس — کے ساتھ بات چیت کر سکتا ہے۔ آپ ایک زنجیر میں سفر نہیں کر سکتے: a.getB().getC().doSomething() — یہ خلاف ورزی ہے۔
LoD اس بارے میں ہے کہ آپ کن آبجیکٹس کے ساتھ بات چیت کر سکتے ہیں (صرف فوری پڑوسی) ۔ Tell, Don’t Ask اس بارے میں ہے کہ کیسے بات چیت کریں (ڈیٹا نہ پوچھیں، کرنے کو کہیں)۔ وہ ایک دوسرے کو پورا کرتے ہیں: LoD بات چیت کے دائرے کو محدود کرتا ہے، Tell Don’t Ask بات چیت کی نیت کو متعین کرتا ہے۔
LoD کو DTOs (ڈیٹا ٹرانسفر آبجیکٹس) اور سادہ ڈیٹا سٹرکچرز کے لیے خلاف ورزی کی جا سکتی ہے جن میں کوئی منطق نہ ہو۔ علاوہ ازیں، Builder پیٹرن خلاف ورزی نہیں مانا جاتا کیونکہ ہر کال وہی builder واپس کرتا ہے۔ مستشنیاں: Stream API (map, filter) میں زنجیریں LoD خلاف ورزیاں نہیں ہیں۔
Detekt میں TooManyFunctions کا قاعدہ ہے (بالواسطہ)، لیکن براہ راست زنجیر کی جانچ کے لیے، DataClassShouldBeImmutable کا قاعدہ اور bindingReference کے ذریعے کسٹم جانچیں استعمال کریں۔ CI کو ترتیب دیں: 2 سے زیادہ کالز کی زنجیریں — انتباہ، 3 سے زیادہ — بائلڈ خرابی۔
SwiftLint میں LoD کے لیے کوئی بنا بنایا قاعدہ نہیں ہے، لیکن آپ regex کے ذریعے ایک کسٹم قاعدہ بنا سکتے ہیں: \..+\.\..+\.\..+ جیسی زنجیریں (3+ ڈاٹ کالز)۔ متبادل: nimble_operator کا قاعدہ استعمال کریں اور طویل زنجیریوں کا پتا لگانے کے لیے اسے وسیع کریں۔
خلاصہ
ہم ایک موبائل ایپلیکیشن ٹرنکی تیار کریں گے
IT Sectr 2017 سے اسٹارٹ اپس اور کاروبار کے لیے iOS اور Android ایپلیکیشنز بناتا ہے۔ ہم آپ کو مشورہ دیں گے اور بہترین حل تجویز کریں گے۔
مزید پڑھیں