ডিপেন্ডেন্সি ইনজেকশন (DI) একটি কৌশল যেখানে একটি অবজেক্ট তার ডিপেন্ডেন্সিগুলি বাইরে থেকে গ্রহণ করে, সেগুলি নিজে তৈরি করার পরিবর্তে। DI হল IoC (ইনভার্সন অফ কন্ট্রোল) নীতির একটি বাস্তবায়ন এবং Dagger, Hilt এবং Swinject-এর ভিত্তি। ডিপেন্ডেন্সি ইনজেকশন কোড কাপলিং কমায়, টেস্টিং সহজ করে এবং আর্কিটেকচারকে নমনীয় করে তোলে। Android-এ, DI Google-এর Dagger Hilt-এর মাধ্যমে মানক, iOS-এ — Swinject বা ম্যানুয়াল ইনজেকশনের মাধ্যমে। আরও জানতে Android DI গাইড দেখুন।
মূল পয়েন্ট
ডিপেন্ডেন্সি ইনজেকশন একটি কৌশল যেখানে একটি অবজেক্ট কনস্ট্রাক্টর, সেটার বা ইন্টারফেসের মাধ্যমে ডিপেন্ডেন্সি (সেবা, রিপজিটরি, কনফিগারেশন) গ্রহণ করে, সেগুলি নিজে new দিয়ে তৈরি করার পরিবর্তে। DI-র লক্ষ্য হল ক্লাসগুলির মধ্যে কাপলিং কমানো। যদি একটি ক্লাস নিজেই ডিপেন্ডেন্সি তৈরি করে, তবে এটি নির্দিষ্ট বাস্তবায়নের সাথে শক্তভাবে আবদ্ধ হয়, যা টেস্টিং এবং পরিবর্তনকে কঠিন করে তোলে। DI-র সাথে, ক্লাস একটি বিমূর্ততা (প্রোটোকল/ইন্টারফেস) নিয়ে কাজ করে এবং কংক্রিট বাস্তবায়ন বাইরে থেকে সরবরাহ করা হয়।
ইনজেকশনের তিনটি পদ্ধতি — কনস্ট্রাক্টর ইনজেকশন (init/constructor-এর মাধ্যমে), সেটার ইনজেকশন (প্রপার্টি/setter-এর মাধ্যমে), ইন্টারফেস ইনজেকশন (ইন্টারফেস মেথডের মাধ্যমে)। কনস্ট্রাক্টর ইনজেকশন পছন্দের পদ্ধতি: ডিপেন্ডেন্সিগুলি সিগনেচারে স্পষ্টভাবে দৃশ্যমান এবং অবজেক্ট সর্বদা বৈধ অবস্থায় তৈরি হয়। সেটার ইনজেকশন ডিফল্ট মান সহ ঐচ্ছিক ডিপেন্ডেন্সির জন্য ব্যবহৃত হয়। ইন্টারফেস ইনজেকশন বিরল, প্রধানত DI কন্টেইনারের জন্য।
| DI প্রকার | পদ্ধতি | কখন ব্যবহার করবেন | উদাহরণ |
|---|---|---|---|
| কনস্ট্রাক্টর | ইনিশিয়ালাইজার প্যারামিটার | বাধ্যতামূলক ডিপেন্ডেন্সি | init(service: ServiceProtocol) |
| প্রপার্টি | ক্লাস প্রপার্টি | ঐচ্ছিক ডিপেন্ডেন্সি | var service: ServiceProtocol? |
| মেথড | মেথড প্যারামিটার | অস্থায়ী ডিপেন্ডেন্সি | func doWork(with service: Service) |
DI কন্টেইনার — একটি লাইব্রেরি যা ডিপেন্ডেন্সি তৈরি এবং জীবনচক্র পরিচালনা করে। কন্টেইনারে টাইপ রেজিস্ট্রেশন থাকে (প্রতিটি বিমূর্ত টাইপ একটি কংক্রিট বাস্তবায়নে ম্যাপ করা) এবং সমাধান করা ডিপেন্ডেন্সি সহ অবজেক্ট তৈরি করার জন্য ফ্যাক্টরি থাকে। Android-এ — Dagger/Hilt, iOS-এ — Swinject, Needle, Dip। কন্টেইনার স্কোপ পরিচালনা করতে পারে: সিঙ্গেলটন (প্রতি অ্যাপ্লিকেশনে একটি ইনস্ট্যান্স), ফিচার স্কোপ (প্রতি স্ক্রিন) বা প্রতিটি অনুরোধে নতুন অবজেক্ট।
Dagger Hilt Google-এর Dagger-এর উপর একটি র্যাপার, Android-এর জন্য মানক DI লাইব্রেরি। Hilt Dagger-কে সরল করে: এটি ম্যানুয়াল কম্পোনেন্ট তৈরি দূর করে, @HiltAndroidApp, @AndroidEntryPoint এবং @Module যোগ করে। Hilt Android জীবনচক্রের সাথে সংহত হয়: ViewModel, Activity, Fragment, Service, BroadcastReceiver অ্যানোটেশনের মাধ্যমে ডিপেন্ডেন্সি গ্রহণ করতে পারে। কোড জেনারেশন কম্পাইল সময়ে ঘটে — Dagger কম্পোনেন্ট বাস্তবায়ন তৈরি করে, যা শূন্য runtime ওভারহেড দেয়।
// অ্যাপ্লিকেশন ক্লাস
@HiltAndroidApp
class MyApp : Application()
// মডিউল — নির্ধারণ করে কীভাবে ডিপেন্ডেন্সি তৈরি করতে হয়
@Module
@InstallIn(SingletonComponent::class)
object NetworkModule {
@Provides
@Singleton
fun provideOkHttpClient(): OkHttpClient = OkHttpClient.Builder().build()
@Provides
@Singleton
fun provideApiService(client: OkHttpClient): ApiService {
return Retrofit.Builder()
.baseUrl("https://api.example.com")
.client(client)
.build()
.create(ApiService::class.java)
}
}
// ViewModel কনস্ট্রাক্টরের মাধ্যমে ডিপেন্ডেন্সি গ্রহণ করে
@HiltViewModel
class MainViewModel @Inject constructor(
private val apiService: ApiService
) : ViewModel() {
private val _state = MutableStateFlow(MainState.Loading)
val state: StateFlow<MainState> = _state.asStateFlow()
fun loadData() {
viewModelScope.launch {
_state.value = MainState.Success(apiService.getData())
}
}
}
// Activity — @AndroidEntryPoint DI সক্রিয় করে
@AndroidEntryPoint
class MainActivity : AppCompatActivity() {
private val viewModel: MainViewModel by viewModels()
}
Dagger কম্পোনেন্ট এবং দৃশ্যমানতার ক্ষেত্র — @Singleton (সম্পূর্ণ অ্যাপ্লিকেশন), @ActivityScoped (Activity অনুযায়ী), @FragmentScoped (Fragment অনুযায়ী), @ViewModelScoped (ViewModel অনুযায়ী)। স্কোপের পছন্দ অবজেক্টের জীবনকাল নির্ধারণ করে। @Singleton — প্রতি প্রক্রিয়ায় একটি ইনস্ট্যান্স, OkHttpClient এবং ডাটাবেসের জন্য উপযুক্ত। @ActivityScoped — অবজেক্ট ততক্ষণ বেঁচে থাকে যতক্ষণ Activity বেঁচে থাকে, স্ক্রিন-স্তরের ডিপেন্ডেন্সির জন্য। @ViewModelScoped — Hilt 2.45+-এ নতুন, অবজেক্ট ততক্ষণ বেঁচে থাকে যতক্ষণ ViewModel বেঁচে থাকে, করুটিন স্কোপের জন্য সুবিধাজনক।
Swinject iOS-এর জন্য একটি জনপ্রিয় ওপেন-সোর্স DI ফ্রেমওয়ার্ক। Swinject Container, Assemblies এবং বিভিন্ন স্কোপ প্রদান করে। Dagger-এর বিপরীতে, Swinject রানটাইমে কাজ করে — ডিপেন্ডেন্সিগুলি কোড জেনারেশন ছাড়াই গতিশীলভাবে সমাধান করা হয়। এটি Swinject সেটআপ করা সহজ করে, কিন্তু ডিবাগ করা কঠিন: অমীমাংসিত ডিপেন্ডেন্সি ত্রুটি শুধুমাত্র রানটাইমে দেখা যায়। Swinject কনস্ট্রাক্টর ইনজেকশন, প্রপার্টি ইনজেকশন এবং মেথড ইনজেকশন সমর্থন করে।
import Swinject
// Assembly — রেজিস্ট্রেশনের গ্রুপ
class NetworkAssembly: Assembly {
func assemble(container: Container) {
container.register(NetworkServiceProtocol.self) { _ in
NetworkService()
}.inObjectScope(.container) // singleton
container.register(UserRepositoryProtocol.self) { r in
UserRepository(
networkService: r.resolve(NetworkServiceProtocol.self)!
)
}
}
}
// ViewModel কনস্ট্রাক্টর ইনজেকশনের মাধ্যমে
class ProfileViewModel: ObservableObject {
private let repository: UserRepositoryProtocol
init(repository: UserRepositoryProtocol) {
self.repository = repository
}
@Published var user: User?
func loadUser() {
repository.fetchUser { [weak self] user in
self?.user = user
}
}
}
// AppDelegate বা অ্যাপে DI সেটআপ
let assembler = Assembler([NetworkAssembly(), ServiceAssembly()])
let viewModel = assembler.resolver.resolve(ProfileViewModel.self)!
// UIKit ViewController-এর জন্য প্রপার্টি ইনজেকশন
container.register(UserViewController.self) { r in
let vc = UserViewController()
vc.viewModel = r.resolve(ProfileViewModel.self)
return vc
}
Swinject স্কোপ — .transient (প্রতিবার নতুন অবজেক্ট), .container (প্রতি কন্টেইনারে সিঙ্গেলটন), .graph (ডিফল্ট — অবজেক্ট একটি একক ডিপেন্ডেন্সি গ্রাফের মধ্যে ভাগ করা হয়)। iOS অ্যাপ্লিকেশনের জন্য, .container এবং .transient যথেষ্ট। Swinject Assembler-ও সমর্থন করে — মডুলার আর্কিটেকচারের জন্য Assembly-র গ্রুপিং। টেস্টিংয়ের জন্য, Assembly MockAssembly দিয়ে প্রতিস্থাপিত হয়, যা প্রোডাকশন কোড পরিবর্তন না করেই ডিপেন্ডেন্সি প্রতিস্থাপন সক্ষম করে।
DI বনাম Service Locator — উভয় প্যাটার্ন ডিপেন্ডেন্সি ব্যবস্থাপনার সমস্যা সমাধান করে, কিন্তু ভিন্নভাবে। DI অবজেক্টে ডিপেন্ডেন্সি ইনজেক্ট করে; Service Locator একটি বৈশ্বিক নিবন্ধন প্রদান করে যেখান থেকে অবজেক্ট নিজেই ডিপেন্ডেন্সি অনুরোধ করে। DI কনস্ট্রাক্টর (বা সেটার) এর মাধ্যমে ডিপেন্ডেন্সি স্পষ্টভাবে ঘোষণা করে। Service Locator ডিপেন্ডেন্সি লুকিয়ে রাখে — সেগুলি মেথডের ভিতরে অনুরোধ করা হয়, যা সিগনেচারকে কম তথ্যপূর্ণ করে তোলে। DI টেস্ট করা সহজ: কনস্ট্রাক্টরে mock পাস করাই যথেষ্ট। Service Locator-এর প্রতিটি টেস্টের জন্য বৈশ্বিক নিবন্ধন সেটআপ প্রয়োজন।
| বৈশিষ্ট্য | ডিপেন্ডেন্সি ইনজেকশন | Service Locator | ম্যানুয়াল ইনজেকশন |
|---|---|---|---|
| ডিপেন্ডেন্সির স্পষ্টতা | কনস্ট্রাক্টরে | মেথডের ভিতরে লুকানো | স্পষ্ট |
| টেস্টিং | কনস্ট্রাক্টরে Mock | Locator সেটআপ | কনস্ট্রাক্টরে Mock |
| সেটআপ জটিলতা | DI কন্টেইনার প্রয়োজন | বৈশ্বিক নিবন্ধন | ম্যানুয়াল তৈরি |
| Runtime ওভারহেড | Dagger — কম্পাইল-টাইম | Runtime লুকআপ | কোনোটিই নয় |
DI বনাম ম্যানুয়াল ইনজেকশন — DI কন্টেইনার ছাড়া, ডিপেন্ডেন্সি ফ্যাক্টরি বা AppDelegate-এ ম্যানুয়ালি তৈরি করা হয়। 5-10 ক্লাসের জন্য, ম্যানুয়াল ইনজেকশন সহজ — Dagger বা Swinject শেখার প্রয়োজন নেই। 50+ ক্লাসের জন্য, ম্যানুয়াল ইনজেকশন সমস্যাযুক্ত হয়ে ওঠে: 5-6 প্যারামিটারযুক্ত কনস্ট্রাক্টর, জটিল তৈরি ক্রম, কোড পুনরাবৃত্তি। DI কন্টেইনার এই প্রক্রিয়াগুলি স্বয়ংক্রিয় করে এবং স্পষ্ট জীবনচক্র ব্যবস্থাপনা প্রদান করে। কন্টেইনার ছাড়া ম্যানুয়াল ইনজেকশন ছোট প্রকল্প এবং প্রোটোটাইপের জন্য একটি ভাল পছন্দ।
কনস্ট্রাক্টর ইনজেকশন — মানক। বাধ্যতামূলক ডিপেন্ডেন্সির জন্য সর্বদা কনস্ট্রাক্টর ইনজেকশন ব্যবহার করুন। এটি ডিপেন্ডেন্সি স্পষ্ট করে এবং অবজেক্ট সর্বদা কাজ করার জন্য প্রস্তুত থাকে। সেটার ইনজেকশন — শুধুমাত্র ঐচ্ছিক ডিপেন্ডেন্সির জন্য (যেমন, delegate বা listener)। ইন্টারফেস ইনজেকশন — ব্যবহার করবেন না যদি না আপনি নিজের DI লাইব্রেরি লিখছেন। কনস্ট্রাক্টর ইনজেকশন নিশ্চিত করার একমাত্র উপায় যে অবজেক্ট একটি বৈধ অবস্থায় তৈরি হয়েছে।
একটি ক্লাস — একটি দায়িত্ব। যদি একটি ক্লাসের কনস্ট্রাক্টরে 5+ প্যারামিটারের প্রয়োজন হয়, ক্লাসটি সম্ভবত একক দায়িত্ব নীতি লঙ্ঘন করছে। ক্লাসটি কম ডিপেন্ডেন্সি সহ বেশ কয়েকটি ভাগে বিভক্ত করুন। লক্ষণ: যদি আপনি 6টি ভিন্ন সেবা সহ ServiceManager ক্লাস লিখছেন — এটি God Object অ্যান্টিপ্যাটার্ন। ব্যবসায়িক যুক্তি Use Cases (Interactors)-এ স্থানান্তর করুন, প্রতিটিতে 1-2টি ডিপেন্ডেন্সি থাকবে।
// ❌ খারাপ: 6 ডিপেন্ডেন্সি — God Object
class ProfileViewModel @Inject constructor(
private val api: ApiService,
private val db: Database,
private val analytics: Analytics,
private val prefs: Preferences,
private val location: LocationProvider,
private val notification: NotificationManager
)
// ✅ ভাল: 1-2 ডিপেন্ডেন্সি সহ Use Cases
class ProfileViewModel @Inject constructor(
private val loadProfileUseCase: LoadProfileUseCase,
private val trackAnalyticsUseCase: TrackAnalyticsUseCase
)
স্কোপ এবং জীবনচক্র — প্রতিটি ডিপেন্ডেন্সির জন্য সঠিক স্কোপ চয়ন করুন। সিঙ্গেলটন: OkHttpClient, ডাটাবেস, SharedPreferences। ফিচার স্কোপ: রিপজিটরি, Use Cases (যদি তারা স্টেটলেস হয়)। Transient: Value Objects, DateFormatter, পার্সার। স্কোপিং ত্রুটিগুলি একটি সাধারণ সমস্যা: স্ক্রিনের অবস্থা সংরক্ষণকারী সিঙ্গেলটন লিকের কারণ হয়। Android-এ, Hilt-এর @ActivityScoped এটি সমাধান করে; Swinject-এ, সতর্কতার সাথে .container ব্যবহার করুন।
প্রায়শই জিজ্ঞাসিত প্রশ্ন
new ক্লাসগুলির মধ্যে শক্ত কাপলিং তৈরি করে — আপনি কোড পরিবর্তন না করে বাস্তবায়ন পরিবর্তন করতে পারেন না। টেস্টিং কঠিন হয়ে যায়: আপনি প্রকৃত সেবার পরিবর্তে mock ইনজেক্ট করতে পারেন না। SRP লঙ্ঘিত হয়: ক্লাস ব্যবসায়িক যুক্তি এবং ডিপেন্ডেন্সি তৈরি উভয়ের জন্য দায়ী। DI বাইরে থেকে ডিপেন্ডেন্সি ইনজেক্ট করে এবং বিমূর্ততার সাথে কাজ করে এই সমস্যাগুলি সমাধান করে।
Dagger Hilt হল Google-এর মানক, কোড জেনারেশন সহ কম্পাইল-টাইম DI, ভাল পারফরম্যান্স এবং Jetpack সংহতকরণ। Koin হল runtime DI, সেটআপে সহজ, কিন্তু ধীর এবং runtime ত্রুটিসহ। প্রোডাকশন প্রজেক্টের জন্য Hilt বেছে নিন। Koin প্রোটোটাইপ এবং ছোট অ্যাপ্লিকেশনের জন্য উপযুক্ত।
না। iOS-এর জন্য উপলব্ধ: Swinject (runtime, জনপ্রিয়), Needle (Uber-এর কাছ থেকে কম্পাইল-টাইম), Dip (হালকা), Weaver (Sourcery-ভিত্তিক)। Apple কোনো অন্তর্নির্মিত DI কন্টেইনার প্রদান করে না, কিন্তু init-এর মাধ্যমে ম্যানুয়াল ইনজেকশন মানক অভ্যাস। SwiftUI-এর জন্য, বাইরের লাইব্রেরি ছাড়া Environment বা @StateObject-এর মাধ্যমে ম্যানুয়াল DI প্রায়শই যথেষ্ট।
হ্যাঁ। কনস্ট্রাক্টরের মাধ্যমে ম্যানুয়াল ইনজেকশন ফ্রেমওয়ার্ক ছাড়া DI। Service Locator ফ্রেমওয়ার্ক ছাড়া একটি বিকল্প। ফ্যাক্টরি এবং Factory Method-ও DI-র রূপ। ফ্রেমওয়ার্ক (Dagger, Swinject) রুটিন রেজিস্ট্রেশন এবং ডিপেন্ডেন্সি সমাধান স্বয়ংক্রিয় করে, কিন্তু 10-20 ক্লাসের জন্য, ম্যানুয়াল DI যথেষ্ট।
DI একটি কৌশল (টেমপ্লেট) যা ইনভার্সন অফ কন্ট্রোল নীতি বাস্তবায়ন করে। GoF প্যাটার্নের বিপরীতে, DI-র 3-4 ক্লাসের কঠোর কাঠামো নেই। DI ডিপেন্ডেন্সি সংগঠিত করার একটি উপায়, একটি ডিজাইন প্যাটার্ন নয়। DI কন্টেইনার (Dagger, Swinject) হল ফ্রেমওয়ার্ক যা এই কৌশলটি স্বয়ংক্রিয় করে।
সারসংক্ষেপ
আমরা একটি মোবাইল অ্যাপ্লিকেশন টার্নকি তৈরি করব
IT Sectr 2017 সাল থেকে স্টার্টআপ এবং ব্যবসার জন্য iOS এবং Android অ্যাপ্লিকেশন তৈরি করে। আমরা আপনাকে পরামর্শ দেব এবং সেরা সমাধান প্রস্তাব করব।
আরও পড়ুন