stephanenicolas / toothpick

A scope tree based Dependency Injection (DI) library for Java / Kotlin / Android.
Apache License 2.0
1.12k stars 115 forks source link

Support for constructor optional dependency injection #331

Open villela opened 5 years ago

villela commented 5 years ago

Hello, after digging into some TP code and docs I couldn't find a way to achieve that but pardon me if it's possible. I want to do optional injection in the constructor with TP, just like Spring does here using the Optional class: https://dzone.com/articles/optional-dependency-injection-with-spring Is it possible, does it make sense for having that in Toothpick, can this be achieved with Kotlin Nullables and/or Java's Optional in a future release? Currently I'm using property injection for this scenario, works as expected: if TP can provide me an object in the scope it will fill the property, otherwise will leave it as null. (using @JvmField + @Inject in Kotlin code).

stephanenicolas commented 5 years ago

To be honest, I am surprised TP can leave a field as null. TP should never return a null value.

I don't think we are gonna add this feature: TP's motto is to remain simple. And in this case, I would advise to create a new entity where the dependency is not optional, or to use TP.getInstance in code where needed.

Le mer. 8 mai 2019 à 15:07, Matheus Villela notifications@github.com a écrit :

Hello, after digging into some TP code and docs I couldn't find a way to achieve that but pardon me if it's possible. I want to do optional injection in the constructor with TP, just like Spring does here using the Optional class: https://dzone.com/articles/optional-dependency-injection-with-spring Is it possible, does it make sense for having that in Toothpick, can this be achieved with Kotlin Nullables and/or Java's Optional in a future release? Currently I'm using property injection for this scenario, works as expected: if TP can provide me an object in the scope it will fill the property, otherwise will leave it as null. (using @JvmField + @Inject https://github.com/Inject in Kotlin code).

— You are receiving this because you are subscribed to this thread. Reply to this email directly, view it on GitHub https://github.com/stephanenicolas/toothpick/issues/331, or mute the thread https://github.com/notifications/unsubscribe-auth/AAN7PXKLCSEYYBKBJZSDU2LPUNFLVANCNFSM4HLVTD5A .

dlemures commented 5 years ago

@villela could you provide an example of how you use @JvmField + @Inject to achieve that for field injection?

Regarding the request itself, could you give us some use cases where you might need optional dependencies?

villela commented 5 years ago

@stephanenicolas @dlemures I'm sure I could achieve that behaviour with property injection but I can't reproduce it again, maybe it was just some scenario I've got with kapt, having the property but not the member injector generated, or maybe I just made a mistake. Doing the steps again TP always raises an exception if it can't retrieve an instance, just as you expected.

It's fine if you don't want to include it, I really like the minimalist approach from TP. I'll probably solve it by wrapping it over a retriever class that knows the correct scope.

Regarding the use case: it's a scenario were the object is responsible for providing a dependency for its "reincarnation", in the case it's just simple parcelables that represents state. Some pseudo code to give an idea:

class Reincarnable(val args: MyInitializationArgs, val state : Optional<MyPersistableState>) {
  init {
    stateToSaveOnKilledAndWillReincarnate {
       return createObjectOfMyPersistableStateTypeToInjectOnMyReincarnation()
    }
  }
}

I used "val state : Optional" in the example instead MyPersistableState? just to not add another complicated issue on the table.

Think about an activity that shows paged lists of foods, Reincarnable is my Presenter or ViewModel, my args may be the id of a kind of food like fruits. Them I'm on page 7, retrieved from a web api and open the details of apple in another activity. My device has low memory and the activity with the pages is destroyed, ofc I won't save the items of the list in the bundle but at least the state that I was on page 7 I want saved so I can reload the page 7 from web when the activity and its Presenter/ViewModel reincarnates. This example can happen even on single activity apps without fragments (my scenario) when we launch intents to open stuff like camera or even when the app is killer on background.

In the pseudo code example when the system decides to destroy the activity the navigation stack with its arguments and states are saved in the bundle for future recreation.

Not sure if I'm clear enough here, I'm doing a Navigator implementation for a fragmentless single activity app very coupled with TP so it may like a bit strange.

dlemures commented 5 years ago

So you are injecting a configuration that can be there or not. On my side, I'd wrap the logic inside a configuration provider and inject that one instead, but that's just a design choice.

Let us discuss about it. On the one hand, TP is used to inject dependencies and here is used as an parameter provider, but on the other, there might be other situations when it makes sense and we want to provide that flexibility if needed.

villela commented 1 year ago

@dlemures @stephanenicolas just to give a bit more context on this, what I was trying to do with the optional injection was to achieve the same thing Google provided with the recent SavedStateHandle.

Of course a container, or a bundle like they did, solves the issue. But I really feel like the optional injection a more elegant solution that also has other usages.