https://kotlinlang.org logo
Title
h

Henrik Johansson

05/11/2023, 7:48 AM
Am I correct in my understanding that
object
s have all the strengths and weaknesses of a regular Java static singleton?
I am juggling an
object
that is actually a data source and I can't make it work nicely with testing where I have to inject some other than standard values.
Should I just bite down and convert it to a class and pass the instance to wherever it is needed? Like I would in Java.
j

Joffrey

05/11/2023, 8:00 AM
One of the major problems of singletons is that they are accessible from anywhere and people don't inject them, so classes are coupled to the singleton implementation. If you use an interface, and you inject it, you can still declare an object as a default implementation.
h

Henrik Johansson

05/11/2023, 8:03 AM
Really hurting me now that the object is reading system variables to initialize itself but now I am starting some dockers using testcontainers and they get different ports and hosts and such but I can't set any of those system variables.
I think I have to change it. It's just so viral and I will have to change in over 200 places....
Intellij will help for sure but still....
y

Youssef Shoaib [MOD]

05/11/2023, 8:45 AM
If the project is internal or bleeding-edge enough, you might also want to consider using
context
receivers as a mechanism to pass that object around. You'd simply make an
interface
out of the object, use that as a
context(MyInterface)
, and then at the very edge of your program you'd initialize the
MyInterface
object and use it in a
with
to pass it to those objects
h

Henrik Johansson

05/11/2023, 11:03 AM
Any example code I can look at @Youssef Shoaib [MOD]?
It seems interesting so I wouldneed to read up a little
Extension functions? Is it the same?
y

Youssef Shoaib [MOD]

05/11/2023, 11:27 AM
Very similar to extensions yes. I'll try to find some more meaningful example code, but it'd go something like this:
data class Config(val hostName: String, val port: Int)
class ConfigContext(val config: Config)
// Depending on taste, you might want to use just Config as a context, instead of a separate ConfigContext.
// It just depends on whether you want to access hostName directly, or you prefer to call config.hostName

context(ConfigContext)
fun connect() {
  makeConnectionWith("${config.hostName}:${config.port}")
}
context(Config)
fun shutdown() {
  shutdownServer("$hostName:$port")
}

// Usage

fun main() {
  with(ConfigContext(Config(/* query system properties */))) {
    connect()
    if(shouldShutdown) shutdown()
  }
}
j

Johann Pardanaud

05/11/2023, 1:31 PM
@Henrik Johansson

I found this video really interesting

to introduce someone (including me) to context receivers.