What is the right approach when I have a `public` ...
# android
l
What is the right approach when I have a
public
class that depends on
internal
implementations for instantiation from the outer world. Example: _moduleB_:
Copy code
class MyClass(private val InternalA, private val InternalB)
_moduleA_:
Copy code
val myClass = MyClass(?, ?) // unknown reference
MyClass
needs the dependencies but on the other hand the dependencies should not be known to the outer world. I'm currently aware of 2 options: 1. instead of constructor variables, define them as properties 2. public wrapper/factory class or wrapper/factory function that takes care of the instantiation What makes me shaky with first option is a statement from stackoverflow:
Constructor injection is used when the class cannot function without the dependent class.
Property injection is used when the class can function without the dependent class.
MyClass cannot function w/o the dependencies so option 1 is bad according to the statement What would you advice?
s
If only moduleB has access to these internal classes then moduleB then the code to construct the objects has to live in moduleB too. The usual options include defining a public function or factory in moduleB that constructs the class, and that can be used by moduleA. If you use a DI tool like Hilt you can provide MyClass in a hilt module inside of moduleB and include that in hilt module moduleA. The simplest would be
public fun MyClass() = MyClass(<somehow get the dependencies>)
. Then moduleA can just call
val myclass = MyClass() // this is actually the function, not the class
l
@s3rius ok this is the second approach I mentioned. I used the term wrapper instead of factory. I'm using KOIN but I didn't mention DI on purpose because it adds additional abstraction. It's always good to know the fundamental mechanisms. But a factory method would definitive be a solid solution if the following more concrete example is legit: _module B_:
Copy code
// Implementation of IFormatter
class SpecificDataFormatter(
  private val stateMachine: StateMachine, // internal
  private val dao: SpecificDataSource,  // internal
) : IFormatter { }
Do you see a violation of any principle here? Since I'm using Koin I never had to think about it.
s
IMO that's fine. If the dependencies come from a DI source then the factory/constructor shouldn't be available outside of the DI graph either. So in that case I'd just provide the date formatter (as SpecificDataFormatter or IFormatter, depending on your needs) as an additional dependency to koin. A constructing function is more suited to when the dependencies are not tied to anything else. Since koin has
@Factory
you don't need a factory for your formatter either, if you want multiple instances.
l
@s3rius
So in that case I'd just provide the date formatter (as SpecificDataFormatter or IFormatter, depending on your needs) as an additional dependency to koin
Exactly this is the reason why I initially started the discussion. The big disadvantage I see here is that I have to make
StateMachine
and
DAO
public so Koin is able to construct them, when
SpecificDataFormatter/IFormatter
is constructed. Do you have an advice?
s
I'm not 100% certain on the Koin specifics. If you declare the Koin AppModule in moduleB and include it in your koin application in moduleA, you run into that problem? Without trying it out, making these classes
internal
instead of
public
should be enough.
l
If you declare the Koin AppModule in moduleB and include it in your koin application in moduleA, you run into that problem?
This works as you described, even with making
StateMachine
and
Dao
internal but tbh I don't know why it works. I already opened a discussion about this here: https://kotlinlang.slack.com/archives/C67HDJZ2N/p1677000079591509?thread_ts=1676844091.055129&amp;cid=C67HDJZ2N What do I miss?