The following code is from the project https://github.com/skydoves/Pokedex
I can't understand why the author need define a blank interface Repository.
What are the benefit using a blank interface Repository ?
Repository.kt
/** Repository is an interface for configuring base repository classes. */
interface Repository
DetailRepository.kt
class DetailRepository #Inject constructor(
private val pokedexClient: PokedexClient,
private val pokemonInfoDao: PokemonInfoDao
) : Repository {
...
}
MainRepository.kt
class MainRepository #Inject constructor(
private val pokedexClient: PokedexClient,
private val pokemonDao: PokemonDao
) : Repository {
...
}
It is called Marker interface pattern. It is interface without any methods or constants. Usually interfaces like this are created to provide special behaviour for a marked classes. You can find a few interfaces like this in java. For example Cloneable and Serializable.
In case of Pokedex i think the reason is much simpler. It looks like this interface was created just to group all repositories. Repository abstraction is never used in the project. Author is always using specific implementations. Repository interface is redundant but can be useful when we want to find all repositories in the project :)
I believe it's because of generics. If you want to create/use an object with generics, the interface will help you a lot.
Consider the following:
interface Animal
class Dog(name:String): Animal{
fun bark(){
println("woff")
}
}
class Cat(name:String): Animal{
fun meow(){
println("meow")
}
}
class Car(name:String){
fun horn(){
println("beep")
}
}
class Farm{
private val animals = ArrayList<Animals>()
fun addAnimal(animal: Animal){
// Even if you don't need a direct function from animal
// you can still store only animals and not cars!
animals.add(animal)
}
}
class Main{
fun main(){
var farm = Farm()
farm.addAnimal(Dog("rex")) // OK
farm.addAnimal(Cat("luna")) // OK
farm.addAnimal(Car("bentley")) // ERROR
}
}
Like this, you can enforce the developer to use the tool as you thought of it, avoiding populating objects with general-purpose items.
A further thought could be if you wanted to add some functionalities and you see at some point, that your class repository actually needs a function for each implementation. If you already have an interface, you won't have to worry to search for all possible implementations (maybe the implementation is in one project extending yours), as the compiler will tell you what is missing.
Related
New to DI. Let's say there is an interface XYZ in a module ABC, which is being used by the main app project as a dependency. I want to inject that interface XYZ to the MainActivity in the main project. Please see below how I am trying.
ABC Module Contents
XYZ
interface XYZ {
fun init()
}
TestView class implementing interface
class TestView: XYZ {
override fun init(){
}
}
Main project contents
AppModule class
#Module
#InstallIn(SingletonComponent::class)
object AppModule {
#Provides
fun xyz(): XYZ = TestView()
}
MainActivity
#AndroidEntryPoint
class MainActivity : AppCompactActivity() {
#Inject lateinit var xyz : XYZ
override onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
xyz.init()
}
}
Please let me know if there is something wrong with this.
If I use the same interface for another class let's say TestView2 and use it in another activity in main project. Can I provide that view as well in the AppModule class? If yes How will I differentiate it from the first one as Both will be using the same interface?
Thanks in advance.
I'm not a senior dev, so take my words with a grain of salt ;)
Please let me know if there is something wrong with this.
Yes and no (see below)
It will work and some people prefer to provide an interface this way,
however it is better to use #Binds (it generates less code, which makes your app smaller and the build times are quicker)
you can find how to use it here
If I use the same interface for another class let's say TestView2 and use it in another activity in main project. Can I provide that view as well in the AppModule class? If yes How will I differentiate it from the first one as Both will be using the same interface?
If you create 2 provide methods which return the same type, dagger won't know which method to use to provide your dependency, that's why you can name your providers (using the #Named annotation), you can find more about it here
(also, just a comment: Using multiple activities in one application isn't really recommended anymore, and I'm personally against it)
I am using Dagger-Hilt for Dependency Injection, and I am stuck with not knowing how to do field injection inside an abstract class.
// #ViewModelScoped
abstract class BaseUseCase<Params, Return>{
// lateinit var not initiazlied. Cannot be injected
#Inject
lateinit var errorHandler: ErrorHandler
fun execute(#nullable params: Params?=null): Flow<DataState<Return>> = flow {
emit(Datastate.Loading)
emit(executeRealization(params))
...
}.catch{ e->
when(e){
...
is Exception -> {
...
errorHandler.handleError(e.message ?: "Unknown Error")
}
}
}
protected abstract fun executeRealization(#Nullable params: Params?=null): DataState<Return>
}
[DI package]
I provided "ErrorHandler" as a singleton using dagger-hilt (AppModule.kt)
Usecases which extend above BaseUseCase are all written for dagger-hilt (UseCaseModule.kt)
I tried providing or binding BaseUseCase class using dagger-hilt such as BaseUseCaseModule.kt, however since it has type parameters, it cannot be binded and also provided.
Currently i cannot inject errorHandler inside BaseUseCase class, so just written ErrorHandler 'object' and using it statically. (e.g. Object ErrorHandler {})
Question
How to do field injection inside abstract class?
Or Am i missing something?
How to do field injection inside an abstract class?
This is currently not supported.
You can consider refactoring your code in these two approaches.
First Approach
Move the exception/error handling up the chain towards the UI, this would include the approach of ViewModel.
With this, you can constructor inject your error handler, then execute your UseCase and wrap the handler around it.
Let's look at a possible solution, in the sketch, we'll utilize clean architecture approach;
ViewModel.kt
#HiltViewModel
class YourViewModel #Inject constructor(private val errorHandler: ErrorHandler, private val useCase : SpecificUseCase) : ViewModel(){
suspend fun realizationFunction(params : Params?=null) : Flow<DataState<Return>> = flow {
emit(Datastate.Loading)
try{
emit(useCase(params))
}catch(exception : Exception){
errorHandler.handleError(e.message ?: "Unknown Error")
}
}
}
On your specific useCase, I do recommend you use repository pattern to execute your functions in order to separate concerns instead of executing your functions inside the use case.
Second Approach
This approach involves taking the error handler deeper into the chain and constructor injecting your error handler in your repository implementation.
This would give you the chance to run the particular function/service calls inside a try/catch and handle your error in there.
The con of this second approach may include the challenge of returning the error result, but incorporating a resource class will make it seamless - seems like you have one already, DataState.
class YourRepositoryImpl(private val errorHandler: ErrorHandler) : YourRepositoryInterface {
override suspend fun doSomething(params : Params?) : Flow<DataState<Return>> {
//call your function and use the error handler
}
}
This gives you cleaner code and better error handling.
Or Am I missing something?
You may be interested in reading much about app architecture and separation of concerns.
In our "SearchUsecase" we have access to "ShowFtsDao" directly.
Does it violate the Clean Architecture principles? Does it violate the MVVM architecture?
Assuming our intention is to develop a well-built, standard structure, is there anything wrong with this piece of code?
class SearchUsecase #Inject constructor(
private val searchRepository: SearchRepository,
private val showFtsDao: ShowFtsDao,
private val dispatchers: AppCoroutineDispatchers
) : SuspendingWorkInteractor<SearchShows.Params, List<ShowDetailed>>() {
override suspend fun doWork(params: Params): List<ShowDetailed> {
return withContext(dispatchers.io) {
val remoteResults = searchRepository.search(params.query)
if (remoteResults.isNotEmpty()) {
remoteResults
} else {
when {
params.query.isNotBlank() -> showFtsDao.search("*$params.query*")
else -> emptyList()
}
}
}
}
data class Params(val query: String)
}
I believe your use case handles more logic than it needs to.
As a simple explanation I like to think about the components this way:
Sources: RemoteSource (networking), LocalSource (db), optionally MemorySource are an abstraction over your database and networking api and they do the IO thread switching & data mapping (which comes in handy on big projects, where the backend is not exactly mobile driven)
Repository: communicates with the sources, he is responsible for deciding where do you get the data from. I believe in your case if the RemoteSource returns empty data, then you get it from the LocalSource. (you can expose of course different methods like get() or fetch(), where the consumer specifies if it wants the latest data and based on that the repository calls the correct Source.
UseCases: Talk with multiple repositories and combine their data.
Yes, it does.
Because your domain module has access to the data module and actually you've violated the dependency rule.
That rule specifies that something declared in an outer circle must
not be mentioned in the code by an inner circle.
Domain layer must contain interfaces for details (Repositories) which are implemented in the data layer,
and then, they could be injected into the UseCases (DIP).
Usually, I use an Interface for my repositories:
interface UserRepository {
fun add()
fun get(userId): User
.....
}
I have three types of Repositories (InMemory, Database, and Network):
They all implement UserRepository:
UserInMemoryRepository - UserDatabaseRepository - UserNetworkRepository
But my network doesn't have any add user API
What is the best practice here?
Remove my UserRepository interface
Leave an empty add user method
...... (Any better approach)
Leaving add() empty doesn't goes in pair with SOLID principles.
If more than one class should override some method, then you shouldn't remove interface either.
What I would do is create interface:
interface Repository {
fun get(userId): User
}
...
interface UserRepository : Repository {
fun add()
}
Then your "Network" can implement Repository, and the rest of classes implement UserRepository.
Of course naming of interfaces is up to you, but according to 'I' in SOLID it is better to have more dedicated interfaces rather than one multifunctional
im creating a highly modular application, i have a lot of clases that need to be injected, all of them are childs (not direct childs) of the same class, none of them have constructor parameters.
I want to avoid having to create a "#Provides" method for each one of them in my module.
Is there a way to tell dagger to automatically provide all the classes that implement a base interface? Or is it possible to do it myself using reflection?
Im using dagger-android with kotlin
Update: Ill post some code to illustrate
In one of the modules i have this interface
interface ExampleClass: BaseExample {
fun doSomething()
}
}
Then in the main app i implement it
class ExampleClassImpl #Inject constructor() : ExampleClass {
override fun doSomething(){
}
}
The class where i need it is a Viewmodel created with dagger so inject works on the constructor.
class ExampleViewModel #Inject constructor(val exmpl :ExampleClass) : BaseViewModel {
}
I want to inject that ExampleClassImpl, to do that i need to create a #module with a method annotated with #Provides or #Bind and return that class.
Without the provider i get an error at compile time:
error: [Dagger/MissingBinding] com.myapp.ExampleClassImpl cannot be provided without an #Provides-annotated method.
You want to inject ExampleClass, but Dagger only knows about ExampleClassImpl. How would Dagger know that you want that specific subclass?
Moreover, you say you have many subclasses that you want to inject. How would Dagger know which one to provide to a constructor expecting the base class?
If you want ExampleViewModel to get an instance of ExampleClassImpl then you can simply change the declaration to:
class ExampleViewModel #Inject constructor(val exmpl :ExampleClassImpl)
Doing so you lose the ability to swap the constructor argument with a different implementation of ExampleClass.
The alternative is to have one #Named #Provides method per subclass. So something like:
// In your module
#Provides
#Named("impl1")
fun provideExampleClassImpl1(impl: ExampleClassImpl): ExampleClass = impl
// When using the named dependency
class ExampleViewModel #Inject constructor(#Named("impl1") val exmpl :ExampleClass)