Flutter - 로컬 데이터베이스 ③: Singleton, factory
목차
1: Flutter - 로컬 데이터베이스 ①: sqflite(생성, 삽입, 업데이트, 삭제, 쿼리)
sqflite
를 사용하여Specs(id, type, category, method, contents, money, dateTime)
라는 로컬 데이터베이스를 생성- 로컬 데이터베이스에 접근하는
SpecProvider
클래스를 생성하고, DB에 읽고 쓰는 함수들을 정의
2: Flutter - 로컬 데이터베이스 ②: Future(데이터베이스 화면에 띄우기, FutureBuilder)
- DB에 접근하는 것은
async
한 작업 - 이를
Future
키워드를 사용해 다루고,FutureBuilder
로 화면에 값을 보여줌
3: Flutter - 로컬 데이터베이스 ③: Singleton, factory <현재>현재>
- DB 인스턴스를 Singleton으로 생성
4: Flutter - 로컬 데이터베이스 ④: 이미지 파일을 데이터베이스에 저장하고 읽기
- 이미지 파일을 데이터베이스에 저장하고 읽기
그런데, 현재는 데이터베이스를 접근할 때마다 SpecProvider()
로 클래스 인스턴스를 새로 생성한다.
즉, 매번 DB를 새로 접근해서 열게 되는데, 이는 불필요한 작업이다.
한 번 커넥션이 생기면 그걸 다른 데서도 공유를 하는 게 좋을 건데, 이럴 때에 Singleton
을 사용한다.
Singleton
Singleton
클래스의 객체를 하나만 만들어야 할 때 사용된다. 즉 그 클래스의 인스턴스는 하나뿐이고, 이걸 어디에서든 같은 걸 공유하며 사용된다. 이는 자원 낭비를 막을 수 있다.
인스턴스는 final
로 선언하여 변경 불가능하게 한다.
factory
다트 도큐먼트에서 factory
에 대해 알아보자
Use the factory keyword when implementing a constructor that doesn’t always create a new instance of its class. For example, a factory constructor might return an instance from a cache, or it might return an instance of a subtype. Another use case for factory constructors is initializing a final variable using logic that can’t be handled in the initializer list.
매번 클래스의 인스턴스를 새로 생성하지 않는 생성자를 구현할 때 factory
키워드를 사용하도록 한다.
Singleton 클래스 구현하기
class SomeSingleton {
static final SomeSingleton _someSingletonInstance = SomeSingleton._internal();
SomeSingleton._internal(){
// init values...
}
factory SomeSingleton() {
return _someSingletonInstance;
}
}
_someSingletonInstance
라는 객체의 인스턴스를 처음에 만들어두고, factory
키워드로 매번 새로 인스턴스를 생성하지 않게 객체가 생성될 때마다 이미 만들어진 _someSingletonInstance
를 반환한다.
SomeSingleton._internal()
은 처음에 한 번만 실행되는 생성자다. 물론 _internal
부분은 이름을 마음대로 해도 된다. 해당 생성자 안에서 클래스에서 사용될 변수들을 초기화하면 된다.
await, async한 내부 변수
그런데 생성자는 async
할 수 없다. 따라서 데이터베이스와 같이 await
할 필요가 있는 변수들을 await
하지 못한 채 생성자에서 초기화할 경우, 진짜로 초기화되기 전에 클래스 인스턴스가 먼저 반환되는 문제가 생긴다.
데이터베이스를 옳게 한 번만 초기화하는 방법을 스택 오버플로우에서 찾았다.
이전 코드
class SpecProvider {
static late Database _database;
Future<Database?> get database async {
_database = await initDB();
return _database;
}
}
late
키워드는 변수가 늦게 초기화될 때 사용했었다. 그런데 late
는 해당 변수가 언제 초기화 되는지는 알 수 없다고 한다.
바꾼 코드
class SpecProvider {
static final SpecProvider _specProvider = SpecProvider._internal();
SpecProvider._internal(){
// init values...
/*
async cannot be used in constructor,
so change 'get database'
*/
}
factory SpecProvider() {
return _specProvider;
}
static Database? _database;
Future<Database> get database async => _database ??= await initDB();
}
데이터베이스는 late
키워드 사용 대신 static Database? _database;
로 변경한다.
??=
연산자로 null
인지 확인 후, 값이 있으면 그냥 반환하고 없으면 await
할 수 있다.
굿
댓글남기기