Singleton is a very useful pattern, and Kotlin (after Scala) makes it easy to declare singletons
Although, useful in some cases, Singleton has been deprecated many years ago because of its tendency to global state and the problems it imposes in a testing context.
So why should Kotlin support a deprecated pattern natively? It will just encourage people to use Singletons here and there, and become one of those language features that must be un-learned because they advertise a bad practice.
I find this to be an interesting subject, so I'll chime in on the debate, but I can't speak for the Kotlin authors' perspectives.
I don’t think the singleton pattern is deprecated, I see it used in every language I’ve ever used. The idea that you have an object that represents global state isn’t bad in itself (IMO). If your goal is having a modular application, having certain managers accessible globally doesn’t break the modularity of your application.
Keep in mind that a singleton doesn’t necessarily mean that the global interface is a concrete implementation. So when it comes to testing or any other contract where you want the implementation details hidden or mocked, this is still possible with a Singleton.
e.g.
object Managers {
public var popUpManager:PopUpManager by Delegates.lazy // trait public var assetManager:AssetManager by Delegates.lazy // trait public var popUpManager:PopUpManager by Delegates.lazy // trait public var windowManager:WindowManager by Delegates.lazy // trait
}
class Foo() {
public fun poke() { popUpManager.poke(“Poke!”)
}
}
// Bear with me, just an example
class TestFoo() {
Test fun poke() { val mockPopUpManager = MockPopUpManager() Managers.popUpManager = mockPopUpManager
var f = Foo()
f.poke()
assertEquals(1, mockPopUpManager.pokeCount)
}
}
For dependency injection, there are a few popular patterns I’ve seen, and each have their pros and cons. Setter injection, constructor injection, or static access.
Setter injection
Pros: Can have many objects injected with a clean syntax. Injector can provide dependencies based on scope, as opposed to 100% global.
Cons: Often uses reflection to reduce boilerplate. Injections are only available after initialization, which may add complexity
Constructor injection
Pros: Dependencies are immediately available.
Cons: Not scalable for objects with many dependencies
Static access
Pros: Easy to understand and find the implementation. Dependencies are immediately available.
Cons: Impossible to have a scoped dependency.
So while I personally wouldn’t use Singletons in every situation, I see their value and like that there is a language feature that enforces them without boilerplate.
I don't think objects are just for classic singletons, there are other usecases like for nested objects, class objects and extension points.
I'm slowly writing "Kotlin DSL Building Blocks" overview, and here is what I have for objects: https://quip.com/huD9ANE1M5UM
A bit of feedback: the current outline looks like it’s going to mostly be a review of the existing language reference. What I would find more useful is a tutorial on how to build DSLs starting from “here is an example DSL” and then showing how to achieve that, with each feature being explained along with how it’s being used.
The reason is, Kotlin has many interesting features that I can read about in detail on the website. They are carefully chosen to interact with each other in a maximally powerful way. But often it’s not fully clear how best to use them. For instance extension functions are obvious for just smoothing out API warts and adding utility methods, but using them to construct DSLs is less obvious.
First, I’m going to describe building blocks and simple DSLs using just one block and few hints about combining block being desribed with another block. Second, I will add more “chapters” about combining various techniques to achieve this or that goal.
You can think of that Quip pages as an EAP Once we are satisfied with the text and explanation, we’ll start pushing it to website and/or blog for people to learn.
I agree, sometimes it may useful to have global state. The thing is, the Singleton feature allows to have mutable global state, which is even worse than read-only global state. So I suggest if it's too late to remove top-level Singletons at least prohibit mutable state. By top-level Singletons I mean top-level declarations such as:
object Markdown {
var foo:Foo // mutable }
My point is, that when you make it a first-class language feature you declare it officially as best practice, since you are the language designer and must have had something in mind. As a result, users are encouraged and go into anti-patterns, without even realizing. Then, the it takes some years for the community to un-learn the bad practice.
To me, mutable global state which is promoted by the Singleton feature does not go along with the otherwise excellent design philosophy in Kotlin, which encourages immutability as a best practice and makes it the defaut way.