DIP (Dependency Inversion Principle) — SOLID का पाँचवाँ सिद्धांत जो मॉड्यूल के बीच निर्भरताएँ बनाने के नियमों को परिभाषित करता है: उच्च-स्तरीय मॉड्यूल को निम्न-स्तरीय मॉड्यूल पर निर्भर नहीं होना चाहिए, दोनों को एब्स्ट्रैक्शन पर निर्भर होना चाहिए। एब्स्ट्रैक्शन को विवरणों पर निर्भर नहीं होना चाहिए — विवरणों को एब्स्ट्रैक्शन पर निर्भर होना चाहिए। यह सिद्धांत, रॉबर्ट मार्टिन द्वारा Clean Architecture (2017) में वर्णित, शिथिल-युग्मित आर्किटेक्चर का आधार है। इस पुस्तक के अनुसार, निर्भरता व्युत्क्रम का सिद्धांत एप्लिकेशन की परतों के बीच कठोर संबंधों को समाप्त करता है।
मुख्य बिंदु
DIP (Dependency Inversion Principle) निर्भरता व्युत्क्रम का सिद्धांत है जो मॉड्यूल के बीच निर्भरता की दिशा के पारंपरिक दृष्टिकोण को उलट देता है। उच्च-स्तरीय मॉड्यूल (व्यावसायिक तर्क) को सीधे निम्न-स्तरीय मॉड्यूल (डेटाबेस, नेटवर्क, UI) पर निर्भर नहीं होना चाहिए। इसके बजाय, दोनों स्तर उच्च-स्तरीय मॉड्यूल में परिभाषित एब्स्ट्रैक्शन पर निर्भर होते हैं।
DIP का औपचारिक सूत्रीकरण दो नियमों को शामिल करता है: A — उच्च-स्तरीय मॉड्यूल को निम्न-स्तरीय मॉड्यूल पर निर्भर नहीं होना चाहिए, दोनों को एब्स्ट्रैक्शन पर निर्भर होना चाहिए। B — एब्स्ट्रैक्शन को विवरणों पर निर्भर नहीं होना चाहिए, विवरणों को एब्स्ट्रैक्शन पर निर्भर होना चाहिए। दूसरा नियम पहले का अनुसरण है: यदि कोई एब्स्ट्रैक्शन विवरणों पर निर्भर करता है, तो वह उच्च-स्तरीय मॉड्यूल के लिए स्थिर आधार नहीं हो सकता।
DIP के बिना, एक सामान्य आर्किटेक्चर इस प्रकार दिखता है: BusinessLogic → DatabaseRepository — व्यावसायिक तर्क सीधे एक ठोस रिपॉजिटरी पर निर्भर करता है। DIP के साथ: BusinessLogic → DatabaseServiceInterface ← DatabaseRepository। BusinessLogic को DatabaseRepository के अस्तित्व के बारे में पता नहीं है, वह केवल DatabaseService इंटरफ़ेस जानता है, जो व्यावसायिक तर्क के बाहर कार्यान्वित किया जाता है।
व्युत्क्रम का अर्थ है कि नियंत्रण प्रवाह और निर्भरता प्रवाह विपरीत दिशाओं में जाते हैं। नियंत्रण प्रवाह ऊपर से नीचे जाता है: UI → ViewModel → UseCase → Repository। निर्भरता प्रवाह नीचे से ऊपर जाता है: Repository, UseCase में परिभाषित इंटरफ़ेस को लागू करता है। Repository (निम्न-स्तरीय) UseCase (उच्च-स्तरीय) पर निर्भर करता है।
यह व्युत्क्रम DIP और सामान्य परत पृथक्करण के बीच मुख्य अंतर है। पारंपरिक स्तरीय आर्किटेक्चर में, प्रत्येक परत नीचे की परत पर निर्भर करती है। DIP वाले आर्किटेक्चर में, सभी परतें एब्स्ट्रैक्शन पर निर्भर करती हैं, जबकि इन एब्स्ट्रैक्शन का कार्यान्वयन इन्फ्रास्ट्रक्चर परत में होता है, जो DI तंत्र के माध्यम से ऊपरी परतों से “जुड़ा” होता है।
DIP तंत्र उच्च-स्तरीय मॉड्यूल में एब्स्ट्रैक्शन को परिभाषित करके और निम्न-स्तरीय मॉड्यूल में उन्हें लागू करके कार्यान्वित किया जाता है। उच्च-स्तरीय मॉड्यूल अपनी आवश्यक कार्यक्षमता के लिए एक इंटरफ़ेस घोषित करता है। निम्न-स्तरीय मॉड्यूल इस इंटरफ़ेस को लागू करता है। वायरिंग एप्लिकेशन के कम्पोज़ीशन रूट पर होती है।
मौजूदा कोड में DIP शुरू करने की प्रक्रिया: निम्न-स्तरीय मॉड्यूल के लिए इंटरफ़ेस निकालें, इस इंटरफ़ेस को उच्च-स्तरीय मॉड्यूल (या एक अलग एब्स्ट्रैक्शन परत) में ले जाएँ, उच्च-स्तरीय मॉड्यूल की निर्भरता को इंटरफ़ेस का उपयोग करने के लिए फिर से लिखें, निम्न-स्तरीय मॉड्यूल को इस इंटरफ़ेस को लागू करने दें। इन चरणों के बाद, निर्भरता की दिशा उलट गई है।
DIP को कम्पोज़ीशन रूट तंत्र की आवश्यकता होती है — एप्लिकेशन में एक बिंदु जहाँ सभी निर्भरताएँ बनाई और एक साथ जोड़ी जाती हैं। Android में यह Application.get() या Hilt घटक है, iOS में — AppDelegate या SceneDelegate। कम्पोज़ीशन रूट एकमात्र स्थान है जहाँ कोड ठोस कार्यान्वयन के बारे में जानता है।
DIP एप्लिकेशन परतों के बीच आर्किटेक्चरल सीमाएँ बनाता है। जब ViewModel, UserRepository इंटरफ़ेस पर निर्भर करता है, तो प्रेज़ेंटेशन और डोमेन परतों के बीच एक सीमा बनती है: ViewModel (प्रेज़ेंटेशन) को नहीं पता कि डेटा कहाँ से आता है। यह सीमा UserRepository के कार्यान्वयन (Room → REST → Mock) को ViewModel को प्रभावित किए बिना बदलने की अनुमति देती है। जितनी अधिक ऐसी सीमाएँ होंगी, एप्लिकेशन फ्रेमवर्क और लाइब्रेरी परिवर्तनों के प्रति उतना ही अधिक लचीला होगा।
Google द्वारा अनुशंसित Android आर्किटेक्चर में, DIP को UseCases के माध्यम से लागू किया जाता है जो डोमेन परत में रहते हैं और Repository इंटरफ़ेस पर निर्भर करते हैं। RepositoryImpl डेटा परत में हैं और इन इंटरफ़ेस को लागू करते हैं। प्रेज़ेंटेशन परत (ViewModel) UseCases पर निर्भर करती है। निर्भरता की दिशा प्रेज़ेंटेशन से डोमेन, डोमेन से डेटा की ओर जाती है — लेकिन कोई भी परत दूसरी परत के ठोस कार्यान्वयन के बारे में नहीं जानती।
DIP और DI अक्सर भ्रमित होते हैं, लेकिन ये अलग-अलग अवधारणाएँ हैं। DIP एक आर्किटेक्चरल सिद्धांत है (क्या करना है: एब्स्ट्रैक्शन पर निर्भर होना)। DI एक कार्यान्वयन पैटर्न है (यह कैसे करना है: कंस्ट्रक्टर के माध्यम से निर्भरताएँ पास करना)। DIP सवाल का जवाब देता है “मॉड्यूल किस पर आधारित होने चाहिए?”, DI जवाब देता है “ऑब्जेक्ट अपनी निर्भरताएँ कैसे प्राप्त करते हैं?”।
डिपेंडेंसी इंजेक्शन कंस्ट्रक्टर, मेथड या प्रॉपर्टी के माध्यम से किसी ऑब्जेक्ट में निर्भरताएँ इंजेक्ट करने का तरीका है। जब कोई Kotlin क्लास अपने कंस्ट्रक्टर के माध्यम से Repository इंटरफ़ेस प्राप्त करता है — यह DI है। यह तथ्य कि ViewModel क्लास किसी ठोस RoomRepository कार्यान्वयन के बजाय Repository इंटरफ़ेस पर निर्भर करता है — यह DIP है। DI उपकरण है, DIP लक्ष्य है।
आप DI फ्रेमवर्क के बिना DIP का पालन कर सकते हैं: कम्पोज़ीशन रूट में निर्भरताओं का मैन्युअल जुड़ाव भी DI (मैन्युअल DI) है। आप DIP का उल्लंघन करते हुए DI फ्रेमवर्क (Dagger, Hilt, Koin) का उपयोग कर सकते हैं: यदि ViewModel सीधे new() के माध्यम से Repository ऑब्जेक्ट बनाता है — DIP का उल्लंघन हुआ है, भले ही फ्रेमवर्क स्थापित हो। DIP एक आर्किटेक्चरल निर्णय है, DI एक तकनीकी विवरण।
आइए डेटा परत पर DIP लागू करने का Android उदाहरण देखें। DIP के बिना, ViewModel सीधे RoomDatabase और DAO बनाता है। DIP के साथ — ViewModel, UserRepository इंटरफ़ेस पर निर्भर करता है, और ठोस RoomUserRepository कार्यान्वयन बाहरी रूप से प्रदान किया जाता है।
// एब्स्ट्रैक्शन डोमेन परत (उच्च स्तर) का है
interface UserRepository {
fun getUser(id: Int): User
}
// डोमेन परत केवल एब्स्ट्रैक्शन पर निर्भर करती है
class GetUserUseCase(
private val repo: UserRepository
) {
fun execute(id: Int): User = repo.getUser(id)
}
// डेटा परत में कार्यान्वयन डोमेन परत के एब्स्ट्रैक्शन पर निर्भर करता है
class RoomUserRepository(
private val dao: UserDao
) : UserRepository {
override fun getUser(id: Int): User {
return dao.getById(id)
}
}
// कम्पोज़ीशन रूट
class AppModule {
fun provideUserRepository(dao: UserDao): UserRepository {
return RoomUserRepository(dao)
}
}
एक iOS उदाहरण Application Coordinator और नेविगेशन प्रोटोकॉल के साथ:
// डोमेन परत में नेविगेशन एब्स्ट्रैक्शन
protocol AuthNavigation {
func navigateToHome()
func navigateToLogin()
}
// ViewModel एब्स्ट्रैक्शन पर निर्भर करता है, UIKit पर नहीं
final class AuthViewModel {
private let navigation: AuthNavigation
init(navigation: AuthNavigation) {
self.navigation = navigation
}
func onLoginSuccess() {
navigation.navigateToHome()
}
}
// Coordinator (UIKit परत) डोमेन परत प्रोटोकॉल लागू करता है
final class AppCoordinator: AuthNavigation {
func navigateToHome() {
// UIKit नेविगेशन कोड
}
func navigateToLogin() {
// UIKit नेविगेशन कोड
}
}
मुख्य बिंदु: AuthViewModel (डोमेन) को AppCoordinator (UIKit) के अस्तित्व के बारे में पता नहीं है। वह केवल AuthNavigation प्रोटोकॉल जानता है। अगर कल UIKit को SwiftUI से बदल दिया जाए — AuthViewModel में कोई बदलाव की आवश्यकता नहीं है। DIP डोमेन परत को UI फ्रेमवर्क और लाइब्रेरी से स्वतंत्र बनाता है।
Hilt Android के लिए मानक DI उपकरण है, जिसे Google द्वारा अनुशंसित किया गया है। यह Jetpack में निर्मित है, ViewModel, Fragment, Service और अन्य Android घटकों का समर्थन करता है। Hilt @Module, @Provides, @Inject एनोटेशन के माध्यम से कम्पोज़ीशन रूट के निर्माण को स्वचालित करता है। Hilt का उपयोग DIP अनुपालन की गारंटी नहीं देता — UserRepository इंटरफ़ेस को डोमेन परत में परिभाषित किया जाना चाहिए, डेटा परत में नहीं।
Koin Kotlin के लिए एक हल्का DI फ्रेमवर्क है जिसमें कोड जनरेशन या एनोटेशन प्रोसेसिंग नहीं है। Koin का DSL (module, single, factory) सीखने में आसान है, लेकिन निर्भरता जाँच रनटाइम पर होती है, कम्पाइल टाइम पर नहीं। Koin iOS समर्थन के कारण मल्टीप्लेटफ़ॉर्म प्रोजेक्ट्स (KMP) में लोकप्रिय है।
Dagger 2 Hilt का पूर्ववर्ती है, अभी भी बड़े प्रोजेक्ट्स में उपयोग किया जाता है। Dagger कम्पाइल टाइम पर DI कोड जनरेट करता है, जो अधिकतम प्रदर्शन और बिल्ड टाइम पर त्रुटि निदान प्रदान करता है। Hilt Dagger के ऊपर बनाया गया है और एक सरलीकृत API प्रदान करता है। नए प्रोजेक्ट्स के लिए, Google Hilt को प्राथमिक DI फ्रेमवर्क के रूप में अनुशंसित करता है।
DI मॉड्यूल को आर्किटेक्चरल परतों के अनुरूप होना चाहिए और DomainModule, DataModule, PresentationModule में विभाजित होना चाहिए। DomainModule केवल एब्स्ट्रैक्शन और UseCases प्रदान करता है। DataModule एब्स्ट्रैक्शन के लिए कार्यान्वयन प्रदान करता है। PresentationModule ViewModels को UseCases से जोड़ता है। यह संगठन सुनिश्चित करता है कि डोमेन परत इन्फ्रास्ट्रक्चर लाइब्रेरी से स्वतंत्र रहे।
DI फ्रेमवर्क के बीच माइग्रेट करते समय (उदाहरण के लिए, Koin से Hilt में), DomainModule की संरचना नहीं बदलती — केवल DataModule और PresentationModule में वायरिंग के तरीके बदलते हैं। DIP डोमेन तर्क का पृथक्करण सुनिश्चित करता है, जबकि DI फ्रेमवर्क एक तकनीकी वायरिंग तंत्र है।
अक्सर पूछे जाने वाले प्रश्न
DIP आर्किटेक्चरल सीमाओं पर आवश्यक है — एप्लिकेशन परतों के बीच (डोमेन → डेटा, प्रेज़ेंटेशन → डोमेन)। एक ही परत के भीतर, DIP अत्यधिक हो सकता है। उदाहरण के लिए, डोमेन परत के अंदर एक उपयोगिता वर्ग StringFormatter को इंटरफ़ेस की आवश्यकता नहीं है — यदि इसे बदलने का कोई कारण नहीं है।
नहीं। DIP एक सिद्धांत है: मॉड्यूल को एब्स्ट्रैक्शन पर निर्भर होना चाहिए। DI एक पैटर्न है: एक ऑब्जेक्ट बाहर से निर्भरताएँ प्राप्त करता है, उन्हें स्वयं बनाने के बजाय। DI, DIP को लागू करने का एक तरीका है, लेकिन DIP का पालन DI के बिना भी किया जा सकता है (फ़ैक्टरी या सर्विस लोकेटर के माध्यम से)। DI बिना DIP संभव है लेकिन इसका कोई आर्किटेक्चरल मूल्य नहीं है।
इंटरफ़ेस उस मॉड्यूल के होते हैं जो उनका उपयोग करता है, न कि उस मॉड्यूल के जो उन्हें लागू करता है। UserRepository डोमेन परत में घोषित किया जाता है और डेटा परत में लागू किया जाता है। यह DIP का मुख्य नियम है: एब्स्ट्रैक्शन का मालिक उपभोक्ता है, कार्यान्वयन का प्रदाता नहीं।
DIP पृथक परतों पर परीक्षण को संभव बनाता है। एक ViewModel जो UserRepository (इंटरफ़ेस) पर निर्भर करता है, बिना डेटाबेस के मॉक कार्यान्वयन के साथ परीक्षण किया जा सकता है। DIP के बिना, ViewModel RoomUserRepository पर निर्भर होगा और प्रत्येक परीक्षण के लिए डेटाबेस सेटअप की आवश्यकता होगी। DIP + DI परीक्षण के दौरान पूर्ण मॉड्यूल पृथक्करण प्रदान करते हैं।
Hilt Android प्रोजेक्ट्स के लिए मानक विकल्प है, जिसे Google द्वारा अनुशंसित किया गया है। Koin Kotlin Multiplatform प्रोजेक्ट्स के लिए एक विकल्प है। Dagger 2 मौजूदा प्रोजेक्ट्स के लिए है जहाँ Hilt में माइग्रेशन उचित नहीं है। फ्रेमवर्क का चुनाव आर्किटेक्चर स्तर पर DIP का पालन करने की आवश्यकता को समाप्त नहीं करता।
सारांश
हम एक मोबाइल एप्लिकेशन टर्नकी विकसित करेंगे
IT Sectr 2017 से स्टार्टअप और व्यवसायों के लिए iOS और Android एप्लिकेशन बनाता है। हम आपको सलाह देंगे और सर्वोत्तम समाधान प्रस्तावित करेंगे।
यह भी पढ़ें