LSP: ڈیولپمنٹ میں باربرا لسکوف کے متبادل اصول کا جوہر

مصنف: IT Sectr اشاعت: 2026-05-12 مطالعے کا وقت: 9 منٹ

LSP (Liskov Substitution Principle) SOLID کا تیسرا اصول ہے، جو آبجیکٹ اورینٹڈ پروگرامنگ میں صحیح وراثت کی شرائط کی وضاحت کرتا ہے۔ یہ اصول باربرا لسکوف نے 1987 میں وضع کیا تھا اور اس طرح رسمی شکل دی گئی: اگر S، T کا ایک ذیلی قسم ہے، تو T قسم کی اشیاء کو S قسم کی اشیاء سے تبدیل کیا جا سکتا ہے بغیر پروگرام کی خصوصیات کو تبدیل کئے۔ جیسا کہ رابرٹ مارٹن کی کتاب Clean Architecture (2017) میں بیان کیا گیا ہے، متبادل اصول کا تقاضہ ہے کہ ذیلی کلاس بیس کلاس کے معاہدے کو کمزور نہ کرے۔

اہم نکات

  • LSP — لسکوف متبادل اصول، صحیح وراثت کے بارے میں SOLID کا تیسرا اصول
  • ذیلی کلاس کو بیس کلاس کے معاہدے کو برقرار رکھنا چاہیے — پیشگی شرائط اور بعد کی شرائط
  • LSP کی خلاف ورزی «مربع اور مستطیل کا مسئلہ» اور پھینکی گئی استثنیات میں ظاہر ہوتی ہے
  • ترکیب LSP کی تعمیل کے لیے اکثر وراثت سے بہتر ہوتی ہے
  • معاہدے کے ذریعے ڈیزائن (Design by Contract) — LSP کی تصدیق کا ایک رسمی طریقہ

LSP (Liskov Substitution Principle) کیا ہے؟

LSP (Liskov Substitution Principle) متبادل اصول ہے جو باربرا لسکوف نے 1987 میں OOPSLA کانفرنس میں وضع کیا تھا۔ رسمی تعریف: q(x) کو T قسم کی اشیاء x کی ایک قابل ثبوت خاصیت سمجھیں۔ پھر q(y) کو S قسم کی اشیاء y کے لیے قابل ثبوت ہونا چاہیے، جہاں S، T کا ایک ذیلی قسم ہے۔ سادہ الفاظ میں: ذیلی کلاس کی اشیاء کو اس طرح برتاؤ کرنا چاہیے کہ بیس کلاس کے ساتھ کام کرنے والا کوڈ ذیلی کلاس کے ساتھ بھی صحیح طریقے سے کام کرتا رہے۔

عملی طور پر، LSP کا مطلب ہے کہ ذیلی کلاس کو بیس کلاس کے معاہدے کی خلاف ورزی نہیں کرنی چاہیے۔ معاہدے میں پیشگی شرائط (طریقہ کار کو کال کرنے کے لیے کیا ضروری ہے)، بعد کی شرائط (کال کے بعد کیا ضمانت دی جاتی ہے) اور غیر متغیرات (شرائط جو آبجیکٹ کی زندگی بھر برقرار رہتی ہیں) شامل ہیں۔ ذیلی کلاس پیشگی شرائط کو مضبوط کر سکتی ہے یا بعد کی شرائط کو کمزور کر سکتی ہے — یہی LSP کی خلاف ورزی ہے۔

LSP کی خلاف ورزی کی ایک کلاسک مثال ایک مربع ہے جو مستطیل سے وراثت پاتا ہے۔ مستطیل کا setWidth طریقہ کار چوڑائی مقرر کرتا ہے، جبکہ مربع میں یہ چوڑائی اور اونچائی دونوں مقرر کرتا ہے۔ ایک کلائنٹ جو مستطیل کے برتاؤ کی توقع رکھتا ہے (ایک طرف کو تبدیل کرنے سے دوسری متاثر نہیں ہوتی) کو ایک غیر متوقع نتیجہ ملتا ہے۔ مربع مستطیل کا ایک جائز ذیلی قسم نہیں ہے۔

LSP کی رسمی شرائط

LSP صحیح وراثت کے لیے تین شرائط قائم کرتا ہے: ذیلی کلاس کی پیشگی شرائط بیس کلاس کی پیشگی شرائط سے زیادہ مضبوط نہیں ہو سکتیں (ذیلی کلاس زیادہ مطالبہ نہیں کرتی)، ذیلی کلاس کی بعد کی شرائط بیس کلاس کی بعد کی شرائط سے کمزور نہیں ہو سکتیں (ذیلی کلاس کم ضمانت نہیں دیتی)، اور بیس کلاس کے غیر متغیرات کو ذیلی کلاس میں محفوظ رہنا چاہیے۔ یہ شرائط برٹرینڈ میئر کے معاہدے کے ذریعے ڈیزائن کے اصول کے طور پر جانی جاتی ہیں۔

اگر کم از کم ایک شرط کی خلاف ورزی ہوتی ہے، تو پولیمورفزم استعمال کرنے والا کوڈ ناکام ہو سکتا ہے۔ کمپائلر معنوی معاہدوں کی جانچ نہیں کرتا، صرف نحوی معاہدوں کی جانچ کرتا ہے۔ اس لیے، LSP جامد ٹائپنگ کا نہیں، بلکہ آرکیٹیکچرل نظم و ضبط کا معاملہ ہے۔

لسکوف متبادل اصول کیسے کام کرتا ہے

LSP کا طریقہ کار اقسام کے رویاتی مطابقت پر مبنی ہے۔ اگر کلاس S، کلاس T سے وراثت پاتی ہے، تو کلائنٹ کوڈ T کی توقع والی جگہوں پر S کو اپنے رویے کو تبدیل کئے بغیر استعمال کرنے کے قابل ہونا چاہیے۔ اس میں نہ صرف طریقہ کار کے دستخط بلکہ ان کے معنی بھی شامل ہیں۔

LSP ذیلی کلاس کو نیا رویہ شامل کرنے سے منع نہیں کرتا۔ بیس کلاس کے لیے لکھے گئے کوڈ کی توقعات کی خلاف ورزی کرنا منع ہے۔ اگر بیس کلاس ضمانت دیتا ہے کہ save طریقہ کار استثنیات نہیں پھینکتا، تو ذیلی کلاس کو انہیں نہیں پھینکنا چاہیے۔ اگر بیس کلاس ایک غیر منفی قدر لوٹاتا ہے، تو ذیلی کلاس کو منفی قدر نہیں لوٹانی چاہیے۔

حقیقی منصوبوں میں، LSP اکثر ذیلی کلاس کے طریقہ کار میں شرطی منطق شامل کرتے وقت خلاف ورزی کیا جاتا ہے: «اگر شرط — استثنا پھینکیں»، «اگر شرط — null لوٹائیں». ان میں سے ہر ایک «حیرت» پولیمورفزم کو کمزور کرتا ہے اور کلائنٹ کوڈ کو کال کرنے سے پہلے آبجیکٹ کی قسم جانچنے پر مجبور کرتا ہے — جو آبجیکٹ اورینٹڈ ڈیزائن کے بنیادی خیال کے خلاف ہے۔

موبائل منصوبوں میں، ایک عام LSP خلاف ورزی بیس ViewModel بناتے وقت ہوتی ہے۔ اگر BaseViewModel ضمانت دیتا ہے کہ onCleared طریقہ کار تمام وسائل جاری کرتا ہے، اور ایک ذیلی کلاس اس طریقہ کار کو خالی کے طور پر اوور رائڈ کرتی ہے — کوئی بھی کوڈ جو پولیمورفک onCleared کال کے ذریعے وسائل کی صفائی پر انحصار کرتا ہے غلط طریقے سے کام کرے گا۔ LSP کا تقاضہ ہے کہ ذیلی کلاس super.onCleared() کو کال کرے یا خود وہی کام انجام دے۔ LifecycleObserver کے ذریعے ترکیب ایک متبادل ہے جو لائف سائیکل مینجمنٹ میں LSP خلاف ورزی کو ختم کرتا ہے۔

کوڈ میں LSP کی خلاف ورزی کی علامات

LSP خلاف ورزی کے اہم اشارے میں شامل ہیں: طریقہ کار کو کال کرنے سے پہلے instanceof یا is کے ذریعے آبجیکٹ کی قسم جانچنا، خالی طریقہ کار کا نفاذ (stub)، NotImplementedError یا UnsupportedOperationException پھینکنا، قدر کے بجائے null لوٹانا۔ ان میں سے ہر ایک نمونہ اشارہ کرتا ہے کہ ذیلی کلاس ایک جائز ذیلی قسم نہیں ہے۔

ایک اور عام علامت «ہے» (is-a) تعلق کو ماڈل کرنے کے بجائے کوڈ دوبارہ استعمال کرنے کے مقصد سے وراثت ہے۔ کلاس Bird میں fly() طریقہ کار ہے۔ کلاس Penguin، Bird سے وراثت پاتی ہے اور fly() کو خالی یا استثنا پھینکنے والے کے طور پر اوور رائڈ کرتی ہے۔ یہ LSP خلاف ورزی ہے: پینگوئن پرندے کا جائز ذیلی قسم نہیں ہے۔

موبائل ڈیولپمنٹ میں، LSP کی خلاف ورزی stub طریقہ کار کے ساتھ بیس ViewHolder، Fragment یا ViewController کلاسز بناتے وقت ہوتی ہے۔ اگر ایک ذیلی کلاس بیس کلاس کے آدھے طریقہ کار استعمال نہیں کرتی — وراثت غلط منتخب کی گئی ہے۔ ترکیب یا انٹرفیس علیحدگی مسئلہ کو زیادہ درست طریقے سے حل کرتا ہے۔

LSP ٹیسٹ

LSP کی جانچ کے لیے ایک سادہ ٹیسٹ: بیس کلاس کے لیے ایک یونٹ ٹیسٹ لکھیں جو اس کے معاہدے (واپسی کی قدریں، استثنیات، ضمنی اثرات) کی تصدیق کرتا ہو۔ اس ٹیسٹ کو ہر ذیلی کلاس کے لیے چلائیں۔ اگر ٹیسٹ ناکام ہوتا ہے — LSP کی خلاف ورزی ہوئی ہے۔ اس نقطہ نظر کو «بیس کلاس معاہدے کے ذریعے جانچ» کہا جاتا ہے۔

Android منصوبوں میں، ایسا ٹیسٹ ViewModel اور Repository کے لیے مفید ہے۔ اگر BaseViewModel غلطی سے پہلے Loading حالت کی ضمانت دیتا ہے، اور ایک ذیلی کلاس Loading کے بغیر غلطی پھینکتی ہے — ٹیسٹ CI مرحلے میں LSP خلاف ورزی کا پتہ لگائے گا۔

موبائل ڈیولپمنٹ میں LSP کی مثالیں

آئیے ClickListener ہینڈلنگ کے ساتھ ایک Android مثال دیکھتے ہیں۔ LSP خلاف ورزی اس وقت ہوتی ہے جب بیس نفاذ کچھ ضمانت دیتا ہے اور ذیلی کلاس اس کی خلاف ورزی کرتی ہے۔

kotlin
// ضمانت کے ساتھ بیس کلاس: onClick کال کیا جائے گا
open class BaseClickListener {
    open fun onClick(view: View) {
        // بنیادی ہینڈلنگ
    }
}

// LSP خلاف ورزی: ذیلی کلاس ایک شرط شامل کرتی ہے جو استثنا پھینکتی ہے
class RestrictedClickListener : BaseClickListener() {
    override fun onClick(view: View) {
        if (!isLoggedIn) {
            throw IllegalStateException("Not logged in")
        }
        super.onClick(view)
    }
}

// صحیح حل: معاہدے کی خلاف ورزی نہیں ہوئی
class ConditionalClickListener : BaseClickListener() {
    override fun onClick(view: View) {
        if (isLoggedIn) {
            super.onClick(view)
        }
    }
}

DataSource پروٹوکول کے ساتھ ایک iOS مثال ڈیٹا کے بجائے nil لوٹا کر LSP خلاف ورزی کو ظاہر کرتی ہے:

swift
// معاہدے کے ساتھ پروٹوکول: ڈیٹا یا غلطی لوٹاتا ہے
protocol DataProvider {
    func fetchData() async throws -> [String]
}

// LSP خلاف ورزی: غلطی کے بغیر nil لوٹاتا ہے
class SilentFailProvider: DataProvider {
    func fetchData() async throws -> [String] {
        return [] // غلطی کے بجائے خالی صف
    }
}

// صحیح LSP تعمیل
class NetworkProvider: DataProvider {
    func fetchData() async throws -> [String] {
        throw NetworkError.timeout
    }
}

عملی اصول: اگر ایک ذیلی کلاس بیس کلاس کے معاہدے کو پورا نہیں کر سکتی، تو اسے ذیلی کلاس نہیں ہونا چاہیے۔ ایک متبادل کم سے کم معاہدے کے ساتھ ایک انٹرفیس نکالنا اور ہر قسم میں اسے اپنے طریقے سے نافذ کرنا ہے۔

LSP اور وراثت: ترکیب کب منتخب کریں

ترکیب ان حالات میں وراثت سے بہتر ہے جہاں «ہے» (is-a) تعلق مبہم یا مشروط ہو۔ ایک کلاسک مثال: کیا Manager ایک Employee ہے؟ ہاں۔ لیکن کیا Square ایک جائز Rectangle ہے؟ LSP کہتا ہے «نہیں»۔ اگر آپ وراثت کی درستگی کے بارے میں شک میں ہیں — ترکیب منتخب کریں۔

موبائل ڈیولپمنٹ میں، ترکیب اکثر انحصاری انجیکشن کے ذریعے استعمال ہوتی ہے: بیس کلاس سے رویہ وراثت میں لینے کے بجائے، ایک کلاس اسے کنسٹرکٹر کے ذریعے حاصل کرتی ہے۔ ViewModel Repository سے وراثت نہیں پاتا بلکہ اسے ایک انحصار کے طور پر قبول کرتا ہے۔ یہ تعریف کے مطابق LSP خلاف ورزی کو ختم کرتا ہے — کوئی وراثت نہیں، کوئی معاہدہ خلاف ورزی نہیں۔

نشانیاں کہ وراثت کو ترکیب سے بدلنا چاہیے: ذیلی کلاس بیس کلاس کے کچھ طریقہ کار استعمال نہیں کرتی، ذیلی کلاس طریقہ کار کو خالی stub سے اوور رائڈ کرتی ہے، کلائنٹ کوڈ instanceof کے ذریعے آبجیکٹ کی قسم جانچتا ہے۔ ان صورتوں میں، وراثت غلط منتخب کی گئی ہے اور LSP کی خلاف ورزی ہوئی ہے۔

انٹرفیس کے ذریعے حل

انٹرفیس وراثت کے بغیر LSP مسئلہ حل کرتے ہیں: ہر قسم صرف ان طریقہ کار کو نافذ کرتی ہے جن کی اسے ضرورت ہے۔ fly() طریقہ کار کے ساتھ ایک مشترکہ بیس Bird کلاس (جہاں Penguin اڑ نہیں سکتا) کے بجائے — ایک Flyable انٹرفیس جو صرف اڑنے والے پرندے نافذ کرتے ہیں۔ Penguin fly() طریقہ کار کے بغیر Bird کو نافذ کرتا ہے — LSP کی خلاف ورزی نہیں ہوئی۔

Android آرکیٹیکچر میں، یہ نقطہ نظر علیحدہ UseCase انٹرفیس کے ذریعے لاگو کیا جاتا ہے: getAll، getById، save، delete طریقہ کار کے ساتھ ایک بڑے UseCase کے بجائے — علیحدہ GetItemsUseCase، SaveItemUseCase انٹرفیس۔ ایک کلائنٹ صرف ضروری انٹرفیس پر انحصار کرتا ہے، اور کوئی بھی کلاس جو اس انٹرفیس کو نافذ کرتی ہے LSP کے نقطہ نظر سے درست ہے۔

اکثر پوچھے گئے سوالات

LSP سادہ وراثت سے کیسے مختلف ہے؟

وراثت ایک زبان کا طریقہ کار ہے؛ LSP اس طریقہ کار کے صحیح استعمال کا ایک اصول ہے۔ وراثت دستخطی مطابقت (نحو) کی ضمانت دیتی ہے؛ LSP رویاتی مطابقت (معنی) کا تقاضہ کرتا ہے۔ LSP کے بغیر وراثت پولیمورفزم دیتی ہے جو رن ٹائم میں ٹوٹ جاتا ہے۔

کیا ذیلی کلاس میں null ہمیشہ LSP کی خلاف ورزی کرتا ہے؟

اگر بیس کلاس غیر null واپسی کی ضمانت دیتا ہے — ہاں۔ اگر معاہدہ null (اختیاری قدر) کی اجازت دیتا ہے — نہیں۔ LSP null کو منع نہیں کرتا؛ یہ معاہدے کو کمزور کرنے سے منع کرتا ہے۔ بیس کلاس کی دستاویزات کا مطالعہ کریں اور چیک کریں کہ آیا ذیلی کلاس کا معاہدہ مطابقت رکھتا ہے۔

Swift میں LSP پروٹوکول پر کیسے لاگو ہوتا ہے؟

LSP پروٹوکول پر اسی طرح لاگو ہوتا ہے جیسے کلاسز پر۔ ایک پروٹوکول نفاذ کو معنوی معاہدے کی پیروی کرنی چاہیے: اگر ایک پروٹوکول کسی طریقہ کار کو non-throwing کے طور پر بیان کرتا ہے، تو نفاذ کو غلطیاں نہیں پھینکنی چاہئیں۔ Swift اسے کمپائلر سطح پر جانچ نہیں کرتا — ذمہ داری ڈیولپر پر ہے۔

کیا sealed class استعمال کرتے وقت LSP کی خلاف ورزی ہو سکتی ہے؟

Kotlin میں Sealed class ایک خاص معاملہ ہے کیونکہ درجہ بندی بند ہے اور کمپائلر کو معلوم ہے۔ LSP sealed class پر کم حد تک لاگو ہوتا ہے کیونکہ تمام ذیلی اقسام when اظہار میں واضح طور پر درج ہیں۔ ایک sealed ذیلی کلاس کی غلطی مقامی ہوگی، پوشیدہ پولیمورفک غلطی نہیں۔

منصوبے میں LSP کی تعمیل کیسے جانچیں؟

بیس کلاس کے لیے ایک پیرامیٹرائزڈ ٹیسٹ لکھیں جو اس کی تمام ذیلی کلاسز کے لیے چلتا ہو۔ ٹیسٹ اہم رویاتی معاہدوں کی تصدیق کرتا ہے: واپسی کی قدریں، استثنیات، حالتیں۔ اگر ٹیسٹ کسی ایک ذیلی کلاس پر ناکام ہوتا ہے — LSP کی خلاف ورزی ہوئی ہے۔ CI میں، ایسا ٹیسٹ پولیمورفک کوڈ کے رجوع کو روکتا ہے۔

خلاصہ

  • LSP (Liskov Substitution Principle) — متبادل اصول، SOLID میں تیسرا، وراثت کے معنوی مطابقت کے بارے میں
  • ذیلی کلاس کو بیس کلاس کا معاہدہ برقرار رکھنا چاہیے: پیشگی شرائط، بعد کی شرائط اور غیر متغیرات
  • instanceof جانچ اور طریقہ کار کی خالی اوور رائڈنگ LSP خلاف ورزی کی اہم علامات ہیں
  • ترکیب اور انٹرفیس LSP مسئلہ حل کرتے ہیں جہاں وراثت غلط ہے
  • مربع اور مستطیل کا مسئلہ ذیلی قسم کی عدم مطابقت کی ایک کلاسک مثال ہے
  • تمام ذیلی کلاسز کے لیے چلایا جانے والا بیس کلاس کا معاہدہ ٹیسٹ CI میں LSP خلاف ورزی کا پتہ لگاتا ہے
  • Kotlin میں Sealed class بند درجہ بندی کی وجہ سے LSP خطرات کو کم کرتی ہے جو کمپائلر کو معلوم ہے

ہم ایک موبائل ایپلیکیشن ٹرنکی تیار کریں گے

IT Sectr 2017 سے اسٹارٹ اپس اور کاروبار کے لیے iOS اور Android ایپلیکیشنز بناتا ہے۔ ہم آپ کو مشورہ دیں گے اور بہترین حل تجویز کریں گے۔

پروجیکٹ پر بحث کریں

مزید پڑھیں