#oop #design-patterns #kotlin #singleton #repository-pattern
#ооп #шаблоны проектирования #kotlin #синглтон #репозиторий-шаблон
Вопрос:
Этот вопрос касается дизайна ООП (класса / интерфейса).
Я разрабатываю библиотеку Android, а не приложение. Приложение будет использовать эту библиотеку. Эта библиотека разработана шаблоном репозитория.
Один репозиторий и 2 источника данных (локальный, удаленный).
Поскольку локальный источник данных использует «SharedPreference», ему нужен контекст.
Ниже приведен мой интерфейс репозитория и его реализации.
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!!
}
}
}
MyRepositoryImpl реализован с помощью шаблона Singleton.
Потому что его следует использовать в любом месте приложения.
Таким образом, разработчики приложений должны иметь возможность получить экземпляр MyRepository следующим образом:
val myRepository = MyRepositoryImpl.getInstance(context)
val data = myRepository.get("key")
Но это выглядит странно … «getInstance (контекст)».
Я думаю, что это не очень хороший подход.
Есть ли какой-нибудь более умный дизайн, пожалуйста?
Комментарии:
1. Тот факт, что
MyRepositoryImpl
не может быть сконструирован независимо, но нуждается в предоставлении некоторой другой информации, наводит меня на мысль, что на самом деле это не синглтон. (Вот почему это ‘выглядит странно’, или, другими словами, код пахнет!) Я бы предложил либо переименоватьgetInstance()
во что-то, указывающее на это, например, в соответствии сinstanceFor()
, которое потенциально могло бы кэшировать несколько экземпляров, по одному для каждого контекста; или, если контекст может быть только один, дайте классу способ обнаружить контекст для себя и сделать его надлежащим синглтоном.
Ответ №1:
В Kotlin вы можете использовать ключевое слово object для реализации одноэлементного шаблона потокобезопасным способом. Нет необходимости определять метод getInstance в вашем сопутствующем объекте. Просто определите свой MyRepository
класс как object, как показано ниже. Перегрузив оператор invoke, вы сможете передать контекст, полезный для инициализации ваших экземпляров localDataSource и remoteDataSource.
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
}
}
Этот код будет скомпилирован в следующий Java-код (блок статической инициализации) :
public final class MyRepository {
public static final MyRepository INSTANCE;
private SomeSingleton() {
INSTANCE = (MyRepository) this;
}
static {
new MyRepository();
}
}
Таким образом, вы сможете получить экземпляр вашего класса MyRepository как обычный, не объектный класс, просто выполнив:
val repo = MyRepository(this) // this is your context
Каждый метод, который вы определяете в MyRepository
классе, будет доступен, как статический метод Java, поэтому вы сможете вызывать его следующим образом:
MyRepository.myMethod()
Более подробная информация здесь
Комментарии:
1. Но как такому объекту узнать, какой контекст использовать, чтобы получить LocalDataSource, необходимый ему для создания экземпляра MyRepository?
2. Я обновил свой ответ, вы можете использовать перегрузку оператора при вызове функции
3. Спасибо, мне нужно подробнее ознакомиться с ключевым словом ‘operator’ ! 🙂