ISP (Interface Segregation Principle) SOLID کا چوتھا اصول ہے، جو کہتا ہے: کلائنٹس کو ان طریقوں پر انحصار نہیں کرنا چاہیے جو وہ استعمال نہیں کرتے۔ یہ اصول رابرٹ مارٹن نے آبجیکٹ اورینٹڈ سسٹمز کے لیے انٹرفیس ڈیزائن کے تناظر میں وضع کیا تھا۔ جیسا کہ کتاب Clean Architecture (2017) میں بیان کیا گیا ہے، انٹرفیس علیحدگی کا اصول ایک عالمگیر انٹرفیس کے بجائے تنگ مخصوص انٹرفیس بنانے کی ضرورت ہے، جس سے جوڑا کم ہوتا ہے اور تبدیلیاں کرنا آسان ہو جاتا ہے۔
اہم نکات
ISP (Interface Segregation Principle) انٹرفیس علیحدگی کا اصول ہے جو «موٹے» انٹرفیس بنانے سے منع کرتا ہے جس میں ایسے طریقے ہوں جو تمام کلائنٹس استعمال نہیں کرتے۔ ایک درجن طریقوں والے ایک انٹرفیس کے بجائے، کئی چھوٹے انٹرفیس ڈیزائن کیے جاتے ہیں، ہر ایک اپنے کلائنٹ گروپ کے لیے۔
یہ اصول رابرٹ مارٹن نے «انٹرفیس آلودگی» کے مسئلے کے حل کے طور پر متعارف کرایا تھا، جب ایک کلاس کو ان طریقوں کو نافذ کرنے پر مجبور کیا جاتا ہے جن کی اسے ضرورت نہیں صرف اس لیے کہ وہ ایک مشترکہ انٹرفیس میں اعلان کردہ ہیں۔ جامد طور پر ٹائپ کی گئی زبانوں میں، اس کے نتیجے میں خالی نفاذ یا استثنیٰ پھینکنا ہوتا ہے — ISP کی خلاف ورزی کی براہ راست علامت۔
ISP اور SRP ایک دوسرے کی تکمیل کرتے ہیں: SRP کلاس کی ذمہ داری کے بارے میں ہے، ISP انٹرفیس معاہدوں کے بارے میں ہے۔ SRP کہتا ہے «ایک کلاس — تبدیلی کی ایک وجہ»، ISP کہتا ہے «ایک انٹرفیس — ایک کلائنٹ منظرنامہ»۔ ایک ساتھ وہ ایک ماڈیولر فن تعمیر بناتے ہیں جہاں نظام کے ہر عنصر کی واضح حدود ہوتی ہیں۔
Fat Interface — ایک انٹرفیس جس میں کسی خاص کلائنٹ کی ضرورت سے زیادہ طریقے ہوتے ہیں۔ مثال کے طور پر، work، eat، sleep طریقوں والا Worker انٹرفیس۔ ایک روبوٹ کارکن کو eat اور sleep نافذ نہیں کرنا چاہیے، لیکن مجبور کیا جاتا ہے۔ حل یہ ہے کہ Workable، Eatable، Sleepable میں تقسیم کیا جائے۔ ہر کلائنٹ کو بالکل وہی ملتا ہے جس کی اسے ضرورت ہے۔
موبائل ڈیولپمنٹ میں، موٹے انٹرفیس ڈیلیگیٹ پروٹوکولز اور DataSource میں پائے جاتے ہیں۔ ایک پروٹوکول میں دو مختلف منظرناموں (ترمیم + ڈسپلے) کے لیے طریقے ہو سکتے ہیں، حالانکہ ایک مخصوص اسکرین ان میں سے صرف ایک استعمال کرتی ہے۔
ISP کو نافذ کرنا ہر انٹرفیس کے کلائنٹس کا تجزیہ کرنے سے شروع ہوتا ہے۔ اگر دو کلائنٹ ایک ہی انٹرفیس کے طریقوں کے مختلف سیٹ استعمال کرتے ہیں — تو انٹرفیس کو تقسیم کیا جانا چاہیے۔ ہر نیا انٹرفیس ان طریقوں کو گروپ کرتا ہے جو ایک منظرنامے کے تحت ایک ساتھ کال کیے جاتے ہیں۔
تقسیم کا طریقہ کار: اصل انٹرفیس کو کئی تنگ انٹرفیس میں توڑا جاتا ہے، ہر ایک مشترکہ حصہ (اگر کوئی ہے) وراثت میں پاتا ہے۔ کلائنٹس عام انٹرفیس کے بجائے مطلوبہ تنگ انٹرفیس پر انحصار کرنے لگتے ہیں۔ اصل انٹرفیس کو نافذ کرنے والی کلاسیں اب صرف وہی تنگ انٹرفیس نافذ کرتی ہیں جن کی انہیں واقعی ضرورت ہے۔
ایک اہم وضاحت: تقسیم کی ڈگری کلائنٹس کی تعداد اور ان کے منظرناموں سے طے ہوتی ہے۔ ISP زیادہ سے زیادہ تقسیم (ہر ایک میں ایک طریقہ والے مائیکرو انٹرفیس) کی ضرورت نہیں رکھتا۔ اس سے ضرورت سے زیادہ پیچیدگی پیدا ہوگی۔ مقصد کلائنٹس کا غیر ضروری طریقوں پر انحصار ختم کرنا ہے، نہ کہ ہر انٹرفیس کا سائز کم سے کم کرنا۔
ISP کی خلاف ورزی کی اہم علامات میں شامل ہیں: خالی طریقوں (جعلی نفاذ) کے ساتھ انٹرفیس نافذ کرنے والی کلاسیں، نفاذ میں UnsupportedOperationException پھینکنا، پیرامیٹرز یا واپسی کی اقسام کی بڑی تعداد جو کچھ کلائنٹس استعمال نہیں کرتے، اور انٹرفیس میں بار بار تبدیلیاں جو صرف کچھ کلائنٹس کو متاثر کرتی ہیں۔
Android ڈیولپمنٹ میں، ISP کی خلاف ورزی کی ایک عام مثال OnItemClickListener انٹرفیس ہے، جس میں کلک، لمبا کلک اور سوائپ کے لیے طریقے شامل ہیں۔ اگر ایک مخصوص اسکرین صرف کلک استعمال کرتی ہے — تو باقی طریقے خالی رہتے ہیں۔ حل OnItemClickListener، OnItemLongClickListener، OnItemSwipeListener میں تقسیم کرنا ہے۔
iOS ڈیولپمنٹ میں، ISP کی خلاف ورزی UIKit ڈیلیگیٹس میں ظاہر ہوتی ہے: ایک پروٹوکول میں مختلف اجزاء کی حالتوں کے لیے طریقے ہوتے ہیں۔ UITableViewDelegate میں ڈسپلے، انتخاب، ترمیم اور سوائپ ایکشن کے لیے طریقے شامل ہیں۔ ڈویلپر اکثر ایک درجن خالی طریقوں کے ساتھ پورے پروٹوکول کو نافذ کرتے ہیں۔ ذمہ داری کے گروپوں کے مطابق کئی پروٹوکولز میں تقسیم مسئلہ حل کرتی ہے۔
مسئلہ صرف کوڈ کی جمالیات کا نہیں ہے۔ جب انٹرفیس تبدیل ہوتا ہے (ایک نیا طریقہ شامل کیا جاتا ہے)، تمام نافذ کرنے والی کلاسوں کو اپ ڈیٹ کیا جانا چاہیے — یہاں تک کہ وہ بھی جنہیں نئے طریقے کی ضرورت نہیں۔ درجنوں اسکرینوں والی موبائل ڈیولپمنٹ میں، یہ جھڑپی تبدیلیوں کا باعث بنتا ہے۔ ISP ہر کلائنٹ کو ان تبدیلیوں سے الگ کرتا ہے جو اس سے متعلق نہیں ہیں۔
مضمر ISP خلاف ورزی کنفیگریشن پیرامیٹرز کے ذریعے ہوتی ہے۔ اگر کوئی طریقہ بہت سے فیلڈز والی شے قبول کرتا ہے، اور کلائنٹ ان میں سے صرف 2-3 استعمال کرتا ہے — تو یہ تقسیم کا اشارہ ہے۔ متبادل: پیرامیٹرز کے کم سے کم سیٹ والے کئی مخصوص طریقے۔
Android ڈیولپمنٹ میں، تمام ایپلیکیشن سیٹنگز کو پڑھنے اور لکھنے کے لیے ایک SharedPreferencesManager استعمال کرنے پر ISP کی خلاف ورزی ہوتی ہے۔ Fragment جسے صرف تھیم پڑھنے کی ضرورت ہے، وہ مختلف ڈیٹا اقسام کے لیے ایک درجن طریقوں والے عالمی مینیجر پر انحصار حاصل کرتا ہے۔ ThemePreferenceProvider، AuthPreferenceProvider، FeatureFlagProvider میں تقسیم — کنفیگریشن سروس کی سطح پر ISP کا اطلاق۔ ہر فراہم کنندہ میں بالکل وہی طریقے ہوتے ہیں جن کی اس کے کلائنٹس کو ضرورت ہے۔
ڈیٹا کے ساتھ کام کرنے کے لیے انٹرفیس والی Android مثال پر غور کریں۔ ISP کی خلاف ورزی — تمام CRUD آپریشنز کے لیے ایک انٹرفیس، حالانکہ تمام کلائنٹس کو تمام آپریشنز کی ضرورت نہیں ہے۔
// ISP کی خلاف ورزی: موٹا انٹرفیس
interface UserRepository {
fun getAll(): List<User>
fun getById(id: Int): User
fun save(user: User)
fun delete(id: Int)
}
// ISP لاگو کرنے کے بعد: تنگ انٹرفیس
interface UserReader {
fun getAll(): List<User>
fun getById(id: Int): User
}
interface UserWriter {
fun save(user: User)
fun delete(id: Int)
}
// ReadOnlyViewModel لکھنے کے طریقوں پر انحصار نہیں کرتا
class ReadOnlyViewModel(
private val reader: UserReader
)
میڈیا کے ساتھ کام کرنے کے لیے پروٹوکول علیحدگی والی iOS مثال:
// ISP کی خلاف ورزی: تمام میڈیا کام کے لیے ایک پروٹوکول
protocol MediaService {
func play(url: URL)
func pause()
func stop()
func upload(data: Data) async -> URL
func download(url: URL) async -> Data
}
// ISP کے بعد: ذمہ داری کے مطابق پروٹوکولز میں علیحدگی
protocol MediaPlayer {
func play(url: URL)
func pause()
func stop()
}
protocol MediaTransfer {
func upload(data: Data) async -> URL
func download(url: URL) async -> Data
}
// PlayerViewModel ڈاؤن لوڈ طریقوں پر انحصار نہیں کرتا
class PlayerViewModel {
private let player: MediaPlayer
}
عملی نتیجہ: ISP کلائنٹس کو انٹرفیس کے غیر متعلقہ حصوں میں تبدیلیوں سے بچاتا ہے۔ UserRepository کو UserReader اور UserWriter میں تقسیم کرنے کا مطلب ہے کہ save میں تبدیلیاں ReadOnlyViewModel کو متاثر نہیں کرتیں، اور اس کے برعکس۔ ہر کلائنٹ اس فعالیت سے الگ ہوتا ہے جو وہ استعمال نہیں کرتا اور نظام کے دوسرے حصوں کی تبدیلی پر تبدیلی کی ضرورت نہیں ہوتی۔
ISP اور SRP — ایک فطری جوڑی۔ SRP طے کرتا ہے کہ ایک کلاس کے پاس تبدیلی کی ایک وجہ ہونی چاہیے۔ ISP اسی منطق کو انٹرفیس پر لاگو کرتا ہے: ایک انٹرفیس کو ایک کلائنٹ منظرنامے کی خدمت کرنی چاہیے۔ ایک کلاس کئی تنگ انٹرفیس نافذ کر سکتی ہے (ہر ایک ایک ذمہ داری کے مطابق)، جو کئی ذمہ داریوں والے ایک موٹے انٹرفیس سے زیادہ صاف ہے۔
ISP اور OCP بھی متعلقہ ہیں: تنگ انٹرفیس کو بڑھانا آسان ہے۔ ایک تنگ انٹرفیس میں نیا طریقہ شامل کرنے سے صرف اس کے کلائنٹس متاثر ہوتے ہیں۔ موٹے انٹرفیس میں طریقہ شامل کرنے سے تمام کلائنٹس متاثر ہوتے ہیں — ممکنہ طور پر OCP کی خلاف ورزی اگر کلائنٹس اپنا نفاذ تبدیل کرنے پر مجبور ہوں۔
ISP اور DIP مل کر کام کرتے ہیں: DIP تجرید پر انحصار کی ضرورت رکھتا ہے۔ ISP ان تجریدوں کو تنگ اور مرکوز بناتا ہے۔ وسیع انٹرفیس پر انحصار اب بھی ایک تجرید پر انحصار ہے، لیکن ISP کے نقطہ نظر سے ایک «برا» تجرید۔ چار اصول (SRP، OCP، ISP، DIP) «ماڈیولریٹی اہرام» بناتے ہیں: SRP اور ISP حدود طے کرتے ہیں، OCP اور DIP توسیع اور جوڑے کے طریقے طے کرتے ہیں۔
موبائل پروجیکٹس (ماڈیولز، فیچرز، تہوں) میں اجزاء کا فن تعمیر عوامی API کی سطح پر ISP سے فائدہ اٹھاتا ہے۔ ہر ماڈیول ایک مشترکہ اگواڑے کے بجائے اپنے صارفین کے لیے تنگ انٹرفیس برآمد کرتا ہے۔ یہ ماڈیول کے اندرونی نفاذ کو تبدیل کرنے کی اجازت دیتا ہے بغیر ان صارفین کو متاثر کیے جو اس کی فعالیت کا صرف ایک حصہ استعمال کرتے ہیں۔
Clean Architecture والے Android پروجیکٹس میں، ISP کا اطلاق UseCases پر ہوتا ہے: ہر UseCase ایک علیحدہ انٹرفیس ہے جس میں ایک invoke یا execute طریقہ ہے۔ کلائنٹ (ViewModel) پورے ذخیرہ کے بجائے صرف اپنے مطلوبہ UseCase پر انحصار کرتا ہے۔ یہ انحصار کو شفاف اور قابل آزمائش بناتا ہے۔
اکثر پوچھے گئے سوالات
ہاں، ضرورت سے زیادہ تقسیم ممکن ہے۔ ISP ہر طریقے کے لیے ایک انٹرفیس کی ضرورت نہیں رکھتا۔ معیار یہ ہے: کیا کوئی کلائنٹ ہے جسے انٹرفیس کے صرف کچھ طریقوں کی ضرورت ہے؟ اگر تمام کلائنٹس تمام طریقے استعمال کرتے ہیں — تو انٹرفیس کو تقسیم کرنے کی ضرورت نہیں۔ تقسیم کی بہترین سطح حقیقی استعمال کے منظرناموں سے طے ہوتی ہے۔
پیرامیٹر کی سطح پر ISP کا مطلب ہے: ایک فنکشن کو بہت سے فیلڈز والی اشیاء قبول نہیں کرنی چاہئیں اگر وہ ان میں سے صرف ایک حصہ استعمال کرتا ہے۔ اس کے بجائے، صرف ضروری ڈیٹا منتقل کیا جانا چاہیے یا مخصوص انٹرفیس استعمال کیے جانے چاہئیں (مثال کے طور پر، مکمل User کے بجائے Renderable انٹرفیس)۔
LSP درست وراثت اور رویاتی ذیلی قسم کی مطابقت کے بارے میں ہے۔ ISP انٹرفیس ڈیزائن کے بارے میں ہے: کلائنٹس کو ان طریقوں پر انحصار نہیں کرنا چاہیے جو وہ استعمال نہیں کرتے۔ LSP سوال کا جواب دیتا ہے «کیا ذیلی کلاس کو بنیادی کلاس کے بجائے استعمال کیا جا سکتا ہے؟»، ISP جواب دیتا ہے «کیا کلائنٹ کو پورے انٹرفیس کی ضرورت ہے؟»
تنگ انٹرفیس mock اشیاء بنانے کو آسان بناتے ہیں: ٹیسٹ ایک یا دو طریقوں والا mock بناتا ہے، ایک درجن کا نہیں۔ انٹرفیس میں جتنے کم طریقے ہوں گے، اس کے رویے کو اسٹب کرنا اتنا ہی آسان ہوگا۔ یہ ٹیسٹ ڈویلپر پر علمی بوجھ کم کرتا ہے اور mock منطق میں غلطیوں کے امکان کو کم کرتا ہے۔
اگر انٹرفیس مستحکم ہے اور تمام کلائنٹس تمام طریقے استعمال کرتے ہیں — تو تقسیم بے کار ہے۔ ایک عام مثال: Apple کے ڈیزائن کردہ UIKit پروٹوکولز۔ انہیں تقسیم کرنا خطرناک ہے کیونکہ UIKit ڈیلیگیٹ کے مکمل نفاذ کی توقع کرتا ہے۔ ایسے معاملات میں، ISP کی خلاف ورزی API کے استحکام سے جائز ہے۔
خلاصہ
ہم ایک موبائل ایپلیکیشن ٹرنکی تیار کریں گے
IT Sectr 2017 سے اسٹارٹ اپس اور کاروبار کے لیے iOS اور Android ایپلیکیشنز بناتا ہے۔ ہم آپ کو مشورہ دیں گے اور بہترین حل تجویز کریں گے۔
مزید پڑھیں