Reputation: 2809
This question is about the OOP(class/interface) design.
I am developing an android library, not an app. The app will use this library. This library is developed by Repository pattern.
One repository and 2 data sources (local, remote).
Because the local data source uses "SharedPreference", it needs Context.
Below is my repository interface and implements.
interface MyRepository {
fun create(String data)
fun get(key: String): String
}
class MyRepositoryImpl(
private val localDataSource: LocalDataSource,
private val remoteDataSource: RemoteDataSource
): MyRepository {
fun create(String data) {
localDataSource.create(data);
remoteDataSource.create(data);
}
fun get(key: String): String {
// temp code
return localDataSource.get(key)
}
companion object {
private var instance: MyRepository? = null
fun getInstance(context: Context): MyRepository {
if (instance == null) {
val localDataSource: LocalDataSource = LocalDataSourceImpl.getInstance(context)
val remoteDataSource: RemoteDataSource = RemoteDataSourceImpl.getInstance()
instance = MyRepositoryImpl(localDataSource, remoteDataSource)
}
return instance!!
}
}
}
The MyRepositoryImpl is implemented by the Singleton pattern. Because it should be used anywhere in the app. So the app developers should be able to get the instance of MyRepository like:
val myRepository = MyRepositoryImpl.getInstance(context)
val data = myRepository.get("key")
But It looks weird... "getInstance(context)". I think this is not a good approach. Is there any more smart design, please?
Upvotes: 6
Views: 5815
Reputation: 8713
In Kotlin you can use object keyword to implement singleton pattern in a thread-safe way. There's no need to define getInstance method in your companion object. Simply define your MyRepository
class as object like below. By overloading invoke operator you'll be able to pass the context useful to initialize your localDataSource and remoteDataSource instances.
object MyRepository {
private lateinit var localDataSource: LocalDataSource
private lateinit var remoteDataSource: RemoteDataSource
operator fun invoke(context: Context): MyRepository {
//...
localDataSource = LocalDataSourceImpl.getInstance(context)
remoteDataSource = RemoteDataSourceImpl.getInstance(context)
return this
}
}
This code will be compiled to the following java code (static initialization block) :
public final class MyRepository {
public static final MyRepository INSTANCE;
private SomeSingleton() {
INSTANCE = (MyRepository) this;
}
static {
new MyRepository();
}
}
This way you'll be able to get an instance of your MyRepository class like a normal, non-object class, simply by doing:
val repo = MyRepository(this) // this is your context
Each method you define within the MyRepository
class will be accessible like it was a java static method, so you'll be able to call it like:
MyRepository.myMethod()
More details here
Upvotes: 8