(+) Application Layer - DI
get_it ν¨ν€μ§λ₯Ό μ΄μ©ν΄μ μμ½κ² DI λ₯Ό ꡬνν μ μλ€. DI ꡬνμ μμ΄μ get_it ν¨ν€μ§ λ§κ³ λ€λ₯Έ ν¨ν€μ§λ λ§μ΄λ€ μ°λμ§ μ λͺ¨λ₯΄κ² λ€. μ΄κ² κ°μ₯ 보νΈμ μΈ λΌμ΄λΈλ¬λ¦¬λ‘ μκ³ μλ€.
registerSingleton vs registerLazySingleton
final locator = GetIt.instance;
Future<void> init() async {
locator.registerSingleton<T>(T instance);
/// registers a type as Singleton by passing an [instance] of that type
/// that will be returned on each call of [get] on that type
final locator = GetIt.instance;
Future<void> init() async {
locator.registerLazySingleton<T>(FactoryFunc<T> func);
/// registers a type as Singleton by passing a factory function that will be called
/// on the first call of [get] on that type
κ°μμμλ registerLazySingleton λ₯Ό μ¬μ©νκ³ , μ΄ μΈμ λ€λ₯Έ μ ν¬λΈ κ°μ μμλ λμΌνκ² registerLazySingleton λ₯Ό μ¬μ©νλ€.
registerLazySingleton λ λ§ κ·Έλλ lazy νκ² register νλ€λ μλ―ΈμΈλ° μ μ€λͺ μλ μ°μ¬μλ―μ΄ register νλ λμ μμ²΄κ° registerLazySingleton μμλ instance κ° μλλΌ factory function μ΄λ€. λΉμ·ν μλ‘ JPA μμ νλ‘μ κ°μ²΄μ²λΌ 첫λ²μ§Έ call λ λΉλ‘μ μ€μ instance κ° μμ±λλ€κ³ λ³Ό μ μλ€. μλλ chat gpt μμ μ°¨μ΄λ₯Ό λ¬Όμμλ λμ¨ μ€λͺ μ΄λ€.
get_it ν¨ν€μ§λ μμ‘΄μ± μ£Όμ μ ꡬννκΈ° μν ν¨ν€μ§ μ€ νλμ λλ€. μ΄ ν¨ν€μ§μμλ singletonκ³Ό lazySingleton λ κ°μ§ μ’ λ₯μ λ±λ‘ λ°©λ²μ μ 곡ν©λλ€.
GetIt.instance.registerSingleton<MyService>(MyService());
singletonμ μ²μ μ¬μ©λ λ μΈμ€ν΄μ€λ₯Ό μμ±νκ³ , μ΄νμλ νμ λμΌν μΈμ€ν΄μ€λ₯Ό λ°ννλ λ±λ‘ λ°©μμ λλ€. get_it ν¨ν€μ§μμ registerSingleton λ©μλλ₯Ό μ¬μ©νμ¬ λ±λ‘ν μ μμ΅λλ€.
GetIt.instance.registerLazySingleton<MyService>(() => MyService());
azySingletonμ μ²μ μ¬μ©λ λ μΈμ€ν΄μ€λ₯Ό μμ±νκ³ , μ΄νμλ νμ λμΌν μΈμ€ν΄μ€λ₯Ό λ°ννλ λ±λ‘ λ°©μμ λλ€. singletonκ³Όμ μ°¨μ΄μ μ μ²μ μ¬μ©λκΈ° μ κΉμ§ μΈμ€ν΄μ€λ₯Ό μμ±νμ§ μλλ€λ μ μ λλ€. get_it ν¨ν€μ§μμ registerLazySingleton λ©μλλ₯Ό μ¬μ©νμ¬ λ±λ‘ν μ μμ΅λλ€.
μ¦, singletonμ λ±λ‘ μμ μ λ°λ‘ μΈμ€ν΄μ€λ₯Ό μμ±νκ³ , lazySingletonμ μΈμ€ν΄μ€κ° νμν λ μμ±λ©λλ€. λ°λΌμ lazySingletonμ μ ν리μΌμ΄μ μ΄ μμλ λ λ§μ λ©λͺ¨λ¦¬λ₯Ό μ°¨μ§νλ κ²½μ° μ μ©ν©λλ€. μΌλ°μ μΌλ‘ μ ν리μΌμ΄μ μμ μ¬μ©νλ λλΆλΆμ μλΉμ€λ lazySingletonμΌλ‘ λ±λ‘νλ κ²μ΄ μ’μ΅λλ€.
κ°μλ΄ DI μ¬μ© μμ
κ°μμμλ μλμ κ°μ΄ μ²λ¦¬νκ³ μλ€.
final instance = GetIt.instance;
Future<void> initAppModule() async {
final sharedPrefs = await SharedPreferences.getInstance();
// shared prefs instance
instance.registerLazySingleton<SharedPreferences>(() => sharedPrefs);
// app prefs instance
instance
.registerLazySingleton<AppPreferences>(() => AppPreferences(instance()));
// network info
instance.registerLazySingleton<NetworkInfo>(
() => NetworkInfoImpl(DataConnectionChecker()));
// dio factory
instance.registerLazySingleton<DioFactory>(() => DioFactory(instance()));
// app service client
final dio = await instance<DioFactory>().getDio();
instance.registerLazySingleton<AppServiceClient>(() => AppServiceClient(dio));
// remote data source
instance.registerLazySingleton<RemoteDataSource>(
() => RemoteDataSourceImplementer(instance()));
// local data source
instance.registerLazySingleton<LocalDataSource>(
() => LocalDataSourceImplementer());
// repository
instance.registerLazySingleton<Repository>(
() => RepositoryImpl(instance(), instance(), instance()));
}
initLoginModule() {
if (!GetIt.I.isRegistered<LoginUseCase>()) {
instance.registerFactory<LoginUseCase>(() => LoginUseCase(instance()));
instance.registerFactory<LoginViewModel>(() => LoginViewModel(instance()));
}
}
initAppModule μμ application layer μ diλ₯Ό λͺ¨λ μ²λ¦¬ν΄μ£Όκ³ , λ‘κ·ΈμΈ view μμ νμν usecase, viewModel μ initLoginModule μμ λ°λ‘ μ²λ¦¬ν΄μ€λ€. κ·Έλ¦¬κ³ μ΄λ₯Ό μλμ κ°μ΄ resetModules μμ νλ²μ μ²λ¦¬ν΄μ£Όλ©΄μλ route μμλ λ°λ‘ λ λ£μ΄μ£Όκ³ μλ€.
resetModules() {
instance.reset(dispose: false);
initAppModule();
initHomeModule();
initLoginModule();
initRegisterModule();
initForgotPasswordModule();
initStoreDetailsModule();
}
static Route<dynamic> getRoute(RouteSettings routeSettings) {
switch (routeSettings.name) {
case Routes.splashRoute:
return MaterialPageRoute(builder: (_) => SplashView());
case Routes.loginRoute:
initLoginModule();
return MaterialPageRoute(builder: (_) => LoginView());
case Routes.onBoardingRoute:
return MaterialPageRoute(builder: (_) => OnBoardingView());
case Routes.registerRoute:
initRegisterModule();
return MaterialPageRoute(builder: (_) => RegisterView());
case Routes.forgotPasswordRoute:
initForgotPasswordModule();
return MaterialPageRoute(builder: (_) => ForgotPasswordView());
case Routes.mainRoute:
initHomeModule();
return MaterialPageRoute(builder: (_) => MainView());
case Routes.storeDetailsRoute:
initStoreDetailsModule();
return MaterialPageRoute(builder: (_) => StoreDetailsView());
default:
return unDefinedRoute();
}
}
registerFactory
κ·Έλ¦¬κ³ usecase, viewModel μ di μ²λ¦¬μ application layer μμμ di μ²λ¦¬ μ¬μ΄μ κ°μ₯ ν° μ°¨μ΄λΌ νλ€λ©΄ registerFactory() λ₯Ό μ¬μ©νλ€λ κ²μ΄λ€.
/// registers a type so that a new instance will be created on each call of [get] on that type
/// [T] type to register
/// [factoryFunc] factory function for this type
/// [instanceName] if you provide a value here your factory gets registered with that
/// name instead of a type. This should only be necessary if you need to register more
/// than one instance of one type. Its highly not recommended
void registerFactory<T extends Object>(
FactoryFunc<T> factoryFunc, {
String? instanceName,
});
registers a type so that a new instance will be created on each call of [get] on that type
μ€λͺ
κ³Ό κ°μ΄ μ¬μ© λ λλ§λ€ μλ‘μ΄ κ°μ²΄λ₯Ό λ°νν΄μ£Όλλ‘ νκΈ° μν΄μ Factory λ₯Ό λ±λ‘νλ€.
μ¬κΈ°κΉμ§κ° κ°μμμ μ¬μ©λ usecase, viewModel μ λν ꡬν λ°©μμ΄λ€. κ°μμμ λͺ ννκ² μ΄ λΆλΆμ μ λ€λ₯΄κ² ꡬννλκ°λ₯Ό μ€λͺ μ ν΄μ£Όμ§ μκ³ μλ€.
κ²°κ³Όμ μΌλ‘ usecase, viewModel μ λν΄μ registerLazySingleton κ³Ό registerFactory λ₯Ό μ¬μ©νμλ κ°κ° μ΄λ»κ² λ€λ₯΄κ² μ²λ¦¬ λλκ°λ₯Ό μκ°ν΄λ³΄λ©΄ μ’μ κ² κ°λ€. μΌλ¨ registerLazySingleton μ κ²½μ° κ³μ μΌλ κ²μ μ¬νμ©νκ² λλ κ²μ΄κ³ registerFactory λ₯Ό μ¬μ©νλ©΄ μΈ λλ§λ€ μλ‘ λ§λ€μ΄μ μ¬μ©νλ κ²μ΄λ€.
μ΄λ μλ‘ λ§λ€κ² λλ©΄ λΉμ°ν μ§μλ³μμ κ°μ κ°μ²΄μ μνμμ²΄κ° μ λΆ μ΄κΈ°ν λμ΄μ μ²μ μνλ‘ λμκ°λ κ²μ΄κ³ μνλ₯Ό μ μ§μν€κ³ μΆλ€λ©΄ singleton μ μ¬μ©ν΄μΌ ν κ²μ΄λ€. viewModel μμ νΉλ³ν μ μ§ν΄μΌν μνκ° μκ³ , λ°μμμ 보μ¬μ£Όλ λ°μ΄ν° λͺ¨λ μλ²μ λκΈ°νλ‘ λμνλ κ²μ΄λΌλ©΄ κ΅³μ΄ μλ‘ λ§λ€ νμ μμ΄ singleton μ μ¬μ©νλ κ²μ΄ λμ κ² κ°λ€.
λ΄κ° ꡬνν DI μ¬μ© νν
μμ§ νλ‘μ νΈκ° μμ± λ¨κ³κ° μλκ³ μ΄κΈ° ꡬν λ¨κ³λΌμ μ½λ μμ΄ λ§μ§ μμλ° μ΄λ μ λ νμ΄ μ‘νμμ΄μ μΌλ¨ μ½λλ₯Ό λ¨κΈ΄λ€. κ°μμμλ νμν λλ§λ€ init μν¨ λ°λ©΄μ λλ μ λ κ² μΌλ¨ private method λ‘ λΆλ₯λ₯Ό ν΄λ λ€ νλμ λ©μλ(prepareDependencies()) μ λͺ¨λ λ£μ΄λκ³ μ΄λ₯Ό μ± μ€νμ λ°λ‘ μ€νμν€λ ννλ‘ κ΅¬ννλ€.
class Injector {
Injector._();
static GetIt get _instance => GetIt.instance;
static DeviceInfoPlugin get deviceInfoPlugin =>
_instance.get<DeviceInfoPlugin>();
static Future prepareDependencies() async {
_prepareUtils();
_prepareNetworks();
}
static void _prepareUtils() {
_instance.registerLazySingleton<DeviceInfoPlugin>(() => DeviceInfoPlugin());
}
static void _prepareNetworks() {
_instance.registerLazySingleton(
() => TodoApiClient(
clientBaseUrl: Environment.baseUrl,
customInterceptors: [BaseHeaderInterceptor()],
),
);
}
}
void run() async {
WidgetsFlutterBinding.ensureInitialized();
await Injector.prepareDependencies();
runApp(const Application());
}
Last updated