의존성 주입(DI)를 위한 라이브러리로, 객체 간의 의존성을 간단하게 해결할 수 있습니다.
의존성 주입 특유의 장점을 통해 코드 재사용과 테스트를 원활하게 진행할 수 있습니다.
Singleton, Factory, Lazy Singleton 3가지의 유형을 지원합니다.
디자인 패턴 중 하나로, 클래스나 모듈 간의 의존성을 외부에서 주입하는 것을 의미합니다.
이때 의존성이란, 하나의 객체가 다른 객체의 의존하여 동작하는 것을 이야기합니다.
아래 코드에서 예를 들면 Person 객체는 Mouth 객체에 의존합니다, 이때 생성자를 통해 의존성을 주입해 주고 있습니다.
class Mouth {
void eat() {
print('yummy');
}
}
// Person 클래스는 Mouth에 의존하며 생성자 주입.
class Person {
final Mouth mouth;
// 생성자 주입을 통해 Mouth 객체를 받음
Person(this.mouth);
void hungry() {
mouth.eat();
print('Person is hungry');
}
}
더 쉽게 이야기하면 배가 고파 밥을 먹으려 한다면, 밥이 있어야겠죠? 하지만 밥을 만드려면 시간이 오래 걸리고 요리를 못한다면 아무것도 먹지 못할 수 있습니다.
이때 밥을 직접 만들지 않고 레트로트 식품을 먹거나 배달을 시키면 요리를 못하더라도 확정적으로 맛있는 밥을 먹을 수 있게 됩니다.
의존성 주입은 이와 비슷합니다. 의존성 주입을 사용하면 객체가 스스로 의존성을 생성하지 않고, 이미 만들어진 의존성을 사용하기 때문에 서로 덜 의존하게 됩니다. 한 객체의 변경이 다른 객체에 미치는 영향이 줄어 코드가 더 유연하고 분리되어 테스트하기에도 쉬워집니다.
dependencies:
flutter:
sdk: flutter
get_it: ^x.x.x # x.x.x는 패키지 버전입니다.
pubspec.yaml
파일에 get_it
패키지를 추가한 후 flutter pub get
명령를 통해 패키지를 가져옵니다.
공유 대상의 객체를 준비합니다, 저는 3가지 유형의 의존성 주입 제공 방안들을 사용하기 위해 아래와 같은 Model들을 사용할 예정입니다.
class Mouth {
Mouth(){
print('Mouth가 생성되었습니다.');
}
void eat() {
print('yummy');
}
}
class Person {
late Mouth mouth;
Person(){
// 해당 Mouth는 Person 이전에 GetIt에 등록이 되어있어야만 합니다.
// 이러한 형태로 생성자내에서 객체를 가져와 적용하는 것 또한 가능합니다.
mouth = GetIt.I<Mouth>();
print('Person이 생성되었습니다.');
}
void hungry() {
print('Person is hungry');
mouth.eat();
}
}
class Group{
String name;
int id;
Group({required this.name,required this.id}){
print('새 그룹 생성');
}
}
class GetItLocator{
static GetItLocator locator = GetItLocator();
// GetIt 싱긆톤 형식.
void setupSingleton() {
if(!GetIt.instance.isRegistered<Mouth>()){
GetIt.instance.registerSingleton(Mouth());
}
}
// GetIt 팩토리 형식.
void setUpFactory(){
if(!GetIt.instance.isRegistered<Group>()){
// registerFactory의 경우 FactoryFunc 형태로 되어 있어 () => 반환객체를 매개변수로 넘겨야합니다.
GetIt.instance.registerFactory<User>(()=> Group(name: 'get_it', id: 99));
}
}
// GetIt lazy 싱글톤 형식.
void setUpLazySingleton(){
if(!GetIt.instance.isRegistered<Person>()){
GetIt.instance.registerLazySingleton(() => Person());
}
}
}
본 예시의 경우 runApp 이후 GetIt을 통해 적용하므로 중복 등록의 문제 GetIt.instance.isRegistered
을 통해 등록이 되어있는지를 확인 후 등록하도록 적용된 코드입니다.
➕ 이때 GetIt.instance
의 경우 코드를 줄인 GetIt.I
로도 호출이 가능합니다.
GetItLocator.locator.setupSingleton();
// 첫 객체 생성
Mouth mouth1 = GetIt.I<Mouth>();
// 두 번째 객체 생성
Mouth mouth2 = GetIt.I<Mouth>();
// mouth1과 mouth2는 같은 객체인지 확인
print(mouth1 == mouth2); // true
싱글톤으로 적용한 객체를 2번 가져와 비교하면 처음 요청될 때 객체를 생성하고, 그 이후에는 항상 같은 객체를 반환합니다.
코드 진행 후 터미널을 살펴본다면 생성자 문구가 한 번만 진행되었음을 통해 위 사실을 확인할 수 있습니다.
GetItLocator.locator.setUpFactory();
// 첫 객체 생성
Group group1 = GetIt.I<Group>();
// 생성자를 통해 객체 생성 print가 진행됩니다.
// 두 번째 객체 생성
Group group2 = GetIt.I<Group>();
// 생성자를 통해 객체 생성 print가 진행됩니다.
// group1과 group2는 같은 객체인지 확인
print(group1 == group2); // false
매번 새로운 객체를 생성하여 반환하는 팩토리 형식을 Group 객체를 적용합니다.
코드 진행 후 터미널을 살펴본다면 생성자 문구가 두 번 찍힘과 동시에, 두 객체를 비교하는 Print를 통해 같은 객체가 아님을 확인할 수 있습니다.
GetItLocator.locator.setUpFactory();
// 첫 객체 생성
Person person1 = GetIt.I<Person>();
// 첫 생성시 생성자의 Print 문이 출력됩니다.
// 두 번째 객체 생성
Person person2 = GetIt.I<Person>();
// person1과 person2는 같은 객체인지 확인
print(person1 == person2); // false
코드 진행 후 터미널을 살펴본다면 생성자 문구가 첫 생성 시 한 번만 찍힘과 동시에, 두 객체를 비교하는 Print를 통해 같은 객체가 같음을 확인할 수 있습니다.
이때 객체 생성 코드들을 주석 처리 하고 GetItLocator.locator.setUpFactory();
코드로 등록만 진행한다면 생성자 print가 출력되지 않음으로 필요시에만 객체를 생성함을 확인할 수 있습니다.
Get_It에 관한 별도의 코드를 Github 레파지토리에서 확인 가능합니다!