کپلنگ (Coupling) ایک میٹرک ہے جو ظاہر کرتا ہے کہ ایپلیکیشن کا ایک ماڈیول دوسرے پر کتنا منحصر ہے۔ ویکیپیڈیا کے مطابق، نرم کپلنگ (low coupling) ایک اچھی طرح ڈیزائن کردہ نظام کی علامت ہے، جہاں ماڈیولز کو پڑوسیوں کو توڑے بغیر تبدیل کیا جا سکتا ہے۔ موبائل ایپلیکیشنز ڈیزائن کرتے وقت کپلنگ کا انتظام آرکیٹیکٹ کے اہم کاموں میں سے ایک ہے۔
اہم نکات
کپلنگ ایک میٹرک ہے جو یہ طے کرتا ہے کہ ایک ماڈیول یا کلاس کسی دوسرے سے کتنی مضبوطی سے جڑا ہوا ہے۔ ایک ماڈیول جتنا زیادہ دوسرے کی اندرونی ساخت کے بارے میں جانتا ہے، کپلنگ اتنا ہی زیادہ ہوتا ہے اور سسٹم کو تبدیل کرنا اتنا ہی مشکل ہوتا ہے۔ اچھی طرح ڈیزائن کردہ آرکیٹیکچر میں، کپلنگ کم سے کم ہونا چاہیے — ماڈیولز صرف سختی سے متعین انٹرفیس کے ذریعے بات چیت کرتے ہیں۔
کپلنگ کے دو پہلو ہیں: ایفرنٹ (آنے والے انحصار — کتنے ماڈیول اس پر منحصر ہیں) اور ایفرنٹ (جانے والے انحصار — یہ کتنے ماڈیولز پر منحصر ہے)۔ ان میٹرکس کا تجزیہ آرکیٹیکچر میں گرم مقامات کی شناخت میں مدد کرتا ہے جہاں ایک ماڈیول میں تبدیلی بہت سے دوسرے کو متاثر کرے گی۔ IntelliJ Dependency Analyzer اور Xcode Graph جیسے اوزار ان رابطوں کو بصری بناتے ہیں۔
یہ سمجھنا ضروری ہے کہ صفر کپلنگ ناممکن ہے — ماڈیولز کو کسی نہ کسی طرح بات چیت کرنی ہوتی ہے، ورنہ یہ سسٹم نہیں بلکہ الگ تھلگ پروگراموں کا مجموعہ ہے۔ آرکیٹیکٹ کا کام کپلنگ کو قابل انتظام اور شفاف بنانا ہے۔ مثالی: ماڈیولز صرف انٹرفیس کے ذریعے بات چیت کرتے ہیں اور صرف سادہ ڈیٹا منتقل کرتے ہیں، ایک دوسرے کی اندرونی ساخت کے بارے میں جانے بغیر۔ اسے نرم کپلنگ (loose coupling) کہا جاتا ہے۔
چھ اقسام کی کپلنگ بہترین سے بدترین تک ایک پیمانہ بناتی ہیں۔ اس پیمانے کو سمجھنا موجودہ کوڈ کا جائزہ لینے اور ری فیکٹرنگ کی سمت منتخب کرنے میں مدد کرتا ہے۔ زیادہ تر موبائل پروجیکٹس میں مخلوط کپلنگ اقسام ہوتی ہیں، اور آرکیٹیکٹ کا کام مضبوط اقسام کو آہستہ آہستہ کمزور اقسام سے تبدیل کرنا ہے۔
ڈیٹا کپلنگ — ماڈیولز طریقہ کار کے پیرامیٹرز کے ذریعے صرف سادہ ڈیٹا کا تبادلہ کرتے ہیں۔ ماڈیول A، ماڈیول B کے طریقہ کار کو کال کرتا ہے، پریمیٹو یا سادہ ڈھانچے منتقل کرتا ہے، اور نتیجہ حاصل کرتا ہے۔ ماڈیول A نہیں جانتا کہ B اندرونی طور پر کیسے لاگو ہوتا ہے۔ یہ کپلنگ کی سب سے مطلوبہ قسم ہے: یہ تبدیلیوں کے اثرات کو کم سے کم کرتی ہے۔
مثال: EmailValidator.isValid(email: String): Boolean۔ صارف کلاس ایک سٹرنگ منتقل کرتا ہے اور ایک بولین وصول کرتا ہے، ویلڈیٹر کے اندر ریگولر ایکسپریشنز یا توثیق کے قواعد کے بارے میں کوئی معلومات نہیں رکھتا۔ توثیق کی منطق تبدیل کرنے سے صارف کو تبدیل کرنے کی ضرورت نہیں پڑتی — کپلنگ کم سے کم ہے۔ ڈیٹا کپلنگ ایپلیکیشن میں تمام عوامی انٹرفیس کا ہدف ہے۔
سٹیمپ کپلنگ — ماڈیولز مرکب اشیاء کا تبادلہ کرتے ہیں لیکن ان کے صرف کچھ فیلڈز استعمال کرتے ہیں۔ ماڈیول A، calculateDiscount طریقہ کار میں ایک User آبجیکٹ منتقل کرتا ہے، جو صرف user.status استعمال کرتا ہے۔ مسئلہ: اگر User ساخت تبدیل ہوتی ہے (ایک لازمی فیلڈ شامل کیا جاتا ہے)، calculateDiscount ماڈیول تبدیل نہیں ہوتا، لیکن User آبجیکٹ بنانے والا صارف تبدیل ہوتا ہے۔
عملی طور پر، سٹیمپ کپلنگ ناگزیر اور قابل قبول ہے اگر منتقل کردہ آبجیکٹ ایک معیاری ڈیٹا ماڈل (Entity) ہو۔ مسئلہ اس وقت پیدا ہوتا ہے جب ایک ماڈیول صرف ایک فیلڈ کے لیے پوری آبجیکٹ وصول کرتا ہے۔ ایسے معاملات میں، مخصوص قدر براہ راست منتقل کرنا بہتر ہے (data coupling)۔ حل یہ ہے کہ وصول کرنے والے فریق کی فیلڈ کے استعمال کا تجزیہ کیا جائے۔
کنٹرول کپلنگ — ایک ماڈیول دوسرے کو ایک جھنڈی منتقل کرتا ہے جو اس کے رویے کو کنٹرول کرتی ہے (calculate(useNewAlgorithm: Boolean))۔ یہ سٹیمپ کپلنگ سے بھی بدتر ہے کیونکہ صارف ماڈیول کو کال کیے گئے ماڈیول کے اندرونی آپریٹنگ متغیرات جاننے ہوتے ہیں۔ حل: طریقہ کار کو دو میں تقسیم کریں — calculateWithNewAlgorithm() اور calculateWithLegacyAlgorithm()۔
ایکسٹرنل کپلنگ — ماڈیولز کسی بیرونی پروٹوکول، ڈیٹا فارمیٹ یا API پر منحصر ہوتے ہیں۔ تمام ماڈیولز جو ایک ہی JSON کو پارس کرتے ہیں یا ایک ہی ڈیٹابیس کے ساتھ کام کرتے ہیں ان میں external coupling ہوتی ہے۔ اسے مکمل طور پر نہیں روکا جا سکتا، لیکن اسے الگ کیا جا سکتا ہے: بیرونی فارمیٹ اور اندرونی ماڈلز کے درمیان ایک میپنگ پرت بنائیں۔ کامن کپلنگ — ماڈیولز ایک مشترکہ عالمی حالت شیئر کرتے ہیں۔ کونٹینٹ کپلنگ — بدترین قسم، جب ایک ماڈیول دوسرے ماڈیول کے اندرونی ڈیٹا کو براہ راست تبدیل کرتا ہے۔
| کپلنگ کی قسم | سطح | وضاحت |
|---|---|---|
| Data | بہترین | پیرامیٹرز کے ذریعے سادہ ڈیٹا منتقل کرنا |
| Stamp | قابل قبول | جزوی استعمال کے ساتھ اشیاء منتقل کرنا |
| Control | درمیانی | جھنڈیوں کے ذریعے رویے کا کنٹرول |
| External | اعلی | بیرونی پروٹوکول/فارمیٹ پر انحصار |
| Common | بہت اعلی | عالمی حالت کا اشتراک |
| Content | ناقابل قبول | ماڈیول کے اندرونی ڈیٹا کی براہ راست تبدیلی |
data (مثالی) سے content (تباہی) تک کا کپلنگ پیمانہ کوڈ کے جائزوں کے لیے ایک عملی آلہ ہے۔ اگر آپ کسی پروجیکٹ میں common یا content coupling دیکھتے ہیں — تو یہ ترجیحی ری فیکٹرنگ اہداف ہیں۔ Data اور stamp coupling قابل قبول ہیں اور کسی بھی پروجیکٹ میں موجود ہیں، لیکن ان کی مقدار کو کنٹرول کیا جانا چاہیے۔
اعلی کپلنگ ڈویلپمنٹ کو ایک سست عمل میں تبدیل کر دیتا ہے جہاں ہر تبدیلی کے لیے درجنوں ممکنہ طور پر ٹوٹے ہوئے ماڈیولز کی جانچ کرنی پڑتی ہے۔ یہ خاص طور پر موبائل ڈویلپمنٹ میں اہم ہے: پلیٹ فارمز سالانہ (Android API Level, iOS SDK)، لائبریریاں سہ ماہی اور کاروباری ضروریات مسلسل اپ ڈیٹ ہوتی ہیں۔ نرم کپلنگ مسلسل رجعت کے بغیر تبدیلیوں کے اس بہاؤ سے نمٹنے کا واحد طریقہ ہے۔
عملی مثال: ایک موبائل ایپ جہاں تمام اسکرینیں براہ راست NetworkingManager اور DatabaseManager کو درآمد کرتی ہیں۔ HTTP کلائنٹ کو Retrofit سے Ktor (Android) یا URLSession سے Alamofire (iOS) میں تبدیل کرتے وقت، ڈویلپر کو ہر اسکرین کو تبدیل کرنا پڑے گا۔ کم کپلنگ کے ساتھ، NetworkDataSource انٹرفیس کے پیچھے چھپی ایک نفاذ کو تبدیل کرنا کافی ہے — صارفین تبدیلی محسوس نہیں کریں گے۔
یونٹ ٹیسٹنگ پر کپلنگ کا اثر بھی بہت زیادہ ہے۔ اعلی کپلنگ والی کلاس (براہ راست کنسٹرکٹر میں انحصار پیدا کرنا) کو الگ تھلگ نہیں کیا جا سکتا — یہ ڈیٹابیس، نیٹ ورک اور UI کو اپنے ساتھ گھسیٹتی ہے۔ ایسی کلاس کو جانچنے کے لیے، آپ کو ایک ایمولیٹر شروع کرنا ہوگا اور انٹیگریشن ٹیسٹ کا انتظار کرنا ہوگا۔ کم کپلنگ والی کلاس کنسٹرکٹر انجیکشن کے ذریعے انحصار قبول کرتی ہے اور آسانی سے موک کی جا سکتی ہے۔
// اعلی کپلنگ — کلاس خود اپنے انحصار بناتی ہے
class ProfileViewModelHigh {
private val api = RetrofitApi()
private val db = RoomDatabase.getInstance()
private val cache = MemoryCache()
}
// کم کپلنگ — انحصار کنسٹرکٹر کے ذریعے منتقل کیے جاتے ہیں
class ProfileViewModelLow(
private val api: ApiService,
private val db: DatabaseService,
private val cache: CacheService
)
پہلے معاملے میں، ProfileViewModelHigh مخصوص نفاذات سے مضبوطی سے بندھا ہوا ہے — Retrofit کو Ktor سے تبدیل کرنے کے لیے ViewModel کوڈ تبدیل کرنا ضروری ہے۔ دوسرے معاملے میں، ProfileViewModelLow صرف انٹرفیس پر منحصر ہے، جن کے نفاذات باہر سے فراہم کیے جاتے ہیں۔ دوسری کلاس کی جانچ کرنا آسان ہے: موک نفاذات منتقل کریں اور ایمولیٹر کے بغیر منطق کی تصدیق کریں۔
ڈیپنڈنسی انورژن پرنسپل (SOLID میں D) کپلنگ کم کرنے کی بنیاد ہے۔ یہ اصول ٹھوس نفاذات کے بجائے تجرید پر انحصار کرنے کا حکم دیتا ہے۔ ایک کلاس براہ راست RetrofitApi آبجیکٹ بنانے کے بجائے، اسے ApiService انٹرفیس وصول کرنا چاہیے۔ یہ انحصار کو ایک مخصوص لائبریری سے تجرید کی سطح پر منتقل کرتا ہے، جسے صارف کو تبدیل کیے بغیر تبدیل کیا جا سکتا ہے۔
آبزرور پیٹرن (یا اس کے ری ایکٹیو ورژن — StateFlow, Combine Publishers) ڈیٹا سورس اور سبسکرائبرز کے درمیان کپلنگ کم کرتا ہے۔ سبسکرائبر نہیں جانتا کہ ڈیٹا کہاں سے آتا ہے — وہ صرف تبدیلیوں پر ردعمل ظاہر کرتا ہے۔ یہ بھیجنے والے اور وصول کرنے والے کو الگ کرتا ہے: موجودہ سبسکرائبرز کو تبدیل کیے بغیر نیا ڈیٹا سورس شامل کیا جا سکتا ہے۔ EventBus اور SharedFlow اسی اصول پر کام کرتے ہیں۔
برج پیٹرن تجرید کو نفاذ سے الگ کرتا ہے، انہیں آزادانہ طور پر تبدیل ہونے دیتا ہے۔ موبائل ڈویلپمنٹ میں، برج مثال کے طور پر پلیٹ فارم پر منحصر ماڈیولز کے لیے استعمال ہوتا ہے: iOS (Kingfisher, Nuke) اور Android (Glide, Coil) کے لیے مختلف نفاذات کے ساتھ ایک مشترکہ ImageLoader انٹرفیس۔ ImageLoader کے ساتھ کام کرنے والا کوڈ منتخب کردہ لائبریری پر منحصر نہیں ہوتا اور نفاذ کو تبدیل کرکے اسے بدل سکتا ہے۔
ڈیپنڈنسی انجیکشن (DI) موبائل ڈویلپمنٹ میں کپلنگ کم کرنے کا سب سے عملی ٹول ہے۔ ایک کلاس خود اپنے انحصار بنانے کے بجائے، ایک DI کنٹینر (Android کے لیے Hilt, Koin, Dagger؛ iOS کے لیے Swinject, Factory) انہیں باہر سے فراہم کرتا ہے۔ کلاس کنسٹرکٹر، طریقہ کار یا پراپرٹی انجیکشن کے ذریعے انحصار وصول کرتی ہے، ٹھوس نفاذات سے بے خبر رہتی ہے۔
DI کلاس کے انحصار کو واضح طور پر دستاویز کرتا ہے: کنسٹرکٹر کو دیکھ کر سمجھا جا سکتا ہے کہ کلاس کن ماڈیولز کے ساتھ بات چیت کرتی ہے۔ اگر کنسٹرکٹر مختلف تہوں سے 8 پیرامیٹر قبول کرتا ہے — تو یہ ضرورت سے زیادہ کپلنگ کی علامت ہے جس میں ری فیکٹرنگ کی ضرورت ہے۔ اچھی مشق فی کلاس 3-4 سے زیادہ انحصار نہیں ہے۔ زیادہ تعداد واحد ذمہ داری کے اصول کی خلاف ورزی اور ضرورت سے زیادہ کپلنگ کی نشاندہی کرتی ہے۔
DI ٹیسٹنگ کو بھی آسان بناتا ہے: ہر ٹیسٹ کے لیے آپ موک انحصار کے ساتھ ایک کلاس بناتے ہیں، حقیقی ڈیٹابیس یا نیٹ ورک کی ضرورت نہیں ہوتی۔ Flutter میں DI کو Provider، Riverpod یا GetIt کے ذریعے لاگو کیا جاتا ہے۔ فریم ورک سے قطع نظر، مقصد ایک ہے: انحصار کو واضح اور قابل تبدیلی بنا کر ماڈیولز کے درمیان کپلنگ کم کرنا۔ موبائل پروجیکٹ میں DI کا استعمال 2020 کی دہائی سے حقیقی معیار رہا ہے۔
// DI کنٹینر انحصار کا گراف بناتا ہے
protocol AuthServiceProtocol {
func login(email: String, password: String) async throws -> User
}
final class AuthService: AuthServiceProtocol {
func login(email: String, password: String) async throws -> User {
// نفاذ
}
}
// ViewModel مخصوص سروس کے بارے میں نہیں جانتا — صرف پروٹوکول
final class LoginViewModel {
private let auth: AuthServiceProtocol
init(auth: AuthServiceProtocol) {
self.auth = auth
}
}
// DI Container واحد مقام ہے جہاں ٹھوس اقسام بنائی جاتی ہیں
final class DIContainer {
lazy var authService: AuthServiceProtocol = AuthService()
lazy var loginViewModel: LoginViewModel {
LoginViewModel(auth: self.authService)
}
}
یہاں، LoginViewModel صرف AuthServiceProtocol پروٹوکول پر منحصر ہے، کسی مخصوص AuthService پر نہیں۔ نفاذ کو تبدیل کرنا (مثال کے طور پر، Firebase Auth سے کسٹم سرور پر سوئچ کرنا) صرف DIContainer میں تبدیلیوں کی ضرورت ہے۔ AuthServiceProtocol کے تمام صارفین اچھوت رہتے ہیں — کپلنگ تجرید اور DI کے ذریعے کم سے کم کی گئی ہے۔
اکثر پوچھے گئے سوالات
کوہیژن ایک ماڈیول کی اندرونی ہم آہنگی کو ناپتا ہے، جبکہ کپلنگ ماڈیولز کے درمیان بیرونی تعلق کو ناپتا ہے۔ اچھی آرکیٹیکچر اعلی کوہیژن اور کم کپلنگ کی خواہش کرتی ہے۔ یہ میٹرکس الٹا متناسب ہیں: کوہیژن بڑھانے سے عام طور پر کپلنگ کم ہوتا ہے، اور اس کے برعکس بھی۔
Data اور stamp عام ہیں اور کسی بھی پروجیکٹ میں موجود ہیں۔ Control coupling محدود منظرناموں میں قابل قبول ہے (مثال کے طور پر، strategy پیٹرن)۔ External coupling بیرونی APIs کے ساتھ کام کرتے وقت ناگزیر ہے، لیکن اسے میپنگ پرت کے پیچھے الگ کیا جانا چاہیے۔ Common اور content coupling آرکیٹیکچرل مسائل کی علامات ہیں جن میں فوری ری فیکٹرنگ کی ضرورت ہے۔
سٹیٹک اینالیسس ٹولز: IntelliJ IDEA Dependency Matrix, Xcode Graph, Gradle Dependencies رپورٹ, SonarQube۔ میٹرکس: ایفرنٹ کپلنگ (Ca)، ایفرنٹ کپلنگ (Ce)، عدم استحکام (Ce/(Ca+Ce))۔ اعلی عدم استحکام (1 کے قریب) کا مطلب ہے کہ ماڈیول تبدیل کرنا آسان ہے اور اس کا حوالہ دینے والی چیزیں کم ہیں — یہ اچھا ہے۔
انتہائی کم کپلنگ کا مطلب تجرید اور انٹرفیس کی ضرورت سے زیادہ تعداد ہو سکتی ہے جو کوڈ کی نیویگیشن کو پیچیدہ بناتی ہے۔ اگر ہر کلاس کے لیے الگ انٹرفیس بنایا جائے تو، پروگرامر فائلوں کے درمیان چھلانگ لگانے میں وقت ضائع کرتا ہے۔ توازن: ماڈیول کے بیرونی API کے لیے انٹرفیس، لیکن ہر اندرونی مددگار کلاس کے لیے نہیں۔
Strangler Fig تکنیک استعمال کریں — براہ راست کالز کو آہستہ آہستہ انٹرفیس سے تبدیل کریں۔ سب سے زیادہ حوالہ کردہ کلاسز کے لیے انٹرفیس نکال کر شروع کریں۔ پھر DI کنٹینر متعارف کروائیں۔ الگ تھلگ کوڈ کو کریکٹرائزیشن ٹیسٹ سے کور کریں تاکہ یقینی بنایا جا سکے کہ ری فیکٹرنگ سسٹم کے رویے کو تبدیل نہیں کرتی۔
خلاصہ
ہم ایک موبائل ایپلیکیشن ٹرنکی تیار کریں گے
IT Sectr 2017 سے اسٹارٹ اپس اور کاروبار کے لیے iOS اور Android ایپلیکیشنز بناتا ہے۔ ہم آپ کو مشورہ دیں گے اور بہترین حل تجویز کریں گے۔
مزید پڑھیں