sergeshustoff/dikt

Hiding implementation

Closed this issue · 9 comments

Snova zdarova!

Main goal is hide implementation under interface. For example:

class NetworkModule(
    private val httpClient: HttpClient
) {

    val networkInteractor: NetworkInteractor by lazy {
        createNetworkInteractor(httpClient)
    }

    @CreateSingle
    private fun createNetworkInteractor(httpClient: HttpClient): NetworkInteractorImpl

}

I assume that it will work, but firstly I don't want to provide httpClient with module creation. I can put in another module, alright. But also I don't want to write this:

@CreateSingle
    private fun createNetworkInteractor(): NetworkInteractorImpl

I think it's possible to use @UseConstructors and use generated method in my own lazy property:

val networkInteractor: NetworkInteractor by lazy {
         // run generated method
}

The only thing I need is your strong convention about generated method name. What do you think? Or maybe it's too complex and I didn't find more proper way?

Full desirable code:

@UseModules(ClientModule::class)
@UseConstructors(NetworkInteractorImpl::class)
class NetworkModule(
    private val clientModule: ClientModule
) {

    val networkInteractor: NetworkInteractor by lazy {
        doNetworkInteractorImpl() // generated method
    }
}

Zdarova)
The compiler plugin doesn't create any functions, the main idea was that user will only call his own functions and never needs to call something generated by plugin directly (in dagger you sometimes see errors when generated code is called because it wasn't generated yet)

With 1.0.x API there is no other solution for your case, but with 1.1.0 API you'd be able to call resolve() inside the lazy property and get the result you wanted. Though even in this case I'd suggest using API like this

@InjectSingleByConstructor(InteractorImpl::class)
class Module() {
    fun interactor(): Interactor =  resolve<InteractorImpl>()
}

All the singletons are already backed by lazy property, so there is no need to do that manually

Also, it seems that resolve() is only supported in functions for now. That I'll fix sometime before stable version

Well, I had read a little about IR, but if it isn't create any method how is UseConstructors works under the hood. In your example you have to create repository to own useCase object and module uses UseConstructor annotation

It generates bodies of already defined functions and calls all the constructors inside

Got it. So this code:

@UseConstructors(MyRepository::class)
@UseModules(HttpModule::class)
class MyModule(
    private val db: Db, 
    private val httpModule: HttpModule
) {

    @Create
    fun doImportantWorkUsecase(): DoImportantWorkUsecase
}

transformes to:

@UseModules(HttpModule::class)
class MyModule(
    private val db: Db, 
    private val httpModule: HttpModule
) {

    @Create
    fun doImportantWorkUsecase(): DoImportantWorkUsecase {
        val r = MyRepository(...)
        return DoImportantWorkUseCase(r, ...)
    }
}

?

Does IR use reflection in that way?

It's like that, but without additional variable in the middle. IR is an intermediate representation of kotlin during compilation. Plugin alters that and resulting bytecode (or whatever) looks like it was compiled with the 'generated' code. So there is no reflection, just slightly unusual kind of code generation without generating any additional files

Thanks!