Android: шаблон репозитория каково наилучшее решение для создания объекта?

#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’ ! 🙂