121 lines
3.2 KiB
Plaintext
121 lines
3.2 KiB
Plaintext
== Why is this an issue?
|
|
|
|
In Kotlin, object declarations and object expressions are the designed way to implement
|
|
the singleton pattern.
|
|
Because this is a built-in language feature, it should be used
|
|
as an idiom instead of resorting to custom idioms.
|
|
|
|
An alternative is to declare private constructors
|
|
and to provide a single class instance within a companion object.
|
|
However, this idiom is adopted from other languages like Java which do not feature
|
|
the direct declaration of objects. It should not be used in Kotlin.
|
|
|
|
=== What is the potential impact?
|
|
|
|
==== Readability and Understanding
|
|
|
|
This change makes it easier to understand the code
|
|
because this is how singletons are intended to be used in Kotlin.
|
|
When developers share common standards and idioms, they need to spend less effort on understanding each other's code.
|
|
|
|
==== Code Redundancy
|
|
|
|
Using a built-in language feature or a standard API is always better than a custom implementation,
|
|
because the reimplementation of something that already exists is unnecessary.
|
|
|
|
== How to fix it
|
|
|
|
Remove the private constructor of the class and
|
|
remove the companion object or the variable that provides the single instance from the class.
|
|
Replace the class declaration with an object declaration if a name is required for the singleton.
|
|
Replace the class declaration with an object expression if the singleton instance can be anonymous.
|
|
|
|
=== Code examples
|
|
|
|
==== Noncompliant code example
|
|
|
|
[source,kotlin,diff-id=1,diff-type=noncompliant]
|
|
----
|
|
class DeviceManager private constructor() {
|
|
fun instanceMethod() {
|
|
// ...
|
|
}
|
|
|
|
companion object { // Noncompliant, explicit class instance provided
|
|
val instance = DeviceManager()
|
|
}
|
|
}
|
|
----
|
|
|
|
==== Compliant solution
|
|
|
|
[source,kotlin,diff-id=1,diff-type=compliant]
|
|
----
|
|
object DeviceManager { // Compliant, object declaration used
|
|
fun instanceMethod() {
|
|
// ...
|
|
}
|
|
}
|
|
----
|
|
|
|
==== Noncompliant code example
|
|
|
|
[source,kotlin]
|
|
----
|
|
interface LayoutStrategy {
|
|
val instanceProperty: String
|
|
fun instanceMethod()
|
|
}
|
|
----
|
|
|
|
[source,kotlin,diff-id=2,diff-type=noncompliant]
|
|
----
|
|
class CustomLayoutStrategy private constructor(): LayoutStrategy {
|
|
|
|
override val instanceProperty = "Hello, world!"
|
|
|
|
override fun instanceMethod() {
|
|
// ...
|
|
}
|
|
|
|
companion object { // Noncompliant, explicit class instance provided
|
|
private val instance by lazy {
|
|
CustomLayoutStrategy()
|
|
}
|
|
|
|
fun getInstance(): LayoutStrategy = instance
|
|
}
|
|
}
|
|
|
|
fun createUI() {
|
|
val component = Container()
|
|
component.setLayoutStrategy(CustomLayoutStrategy.getInstance())
|
|
}
|
|
----
|
|
|
|
==== Compliant solution
|
|
|
|
[source,kotlin,diff-id=2,diff-type=compliant]
|
|
----
|
|
fun createUI() {
|
|
val component = Container()
|
|
component.setLayoutStrategy(object: LayoutStrategy { // Compliant, object expression used
|
|
|
|
override val instanceProperty = "Hello, world!"
|
|
|
|
override fun instanceMethod() {
|
|
// ...
|
|
}
|
|
})
|
|
}
|
|
----
|
|
|
|
== Resources
|
|
|
|
=== Documentation
|
|
|
|
* https://kotlinlang.org/docs/object-declarations.html[Kotlin Docs, Object expressions and declarations]
|
|
|
|
=== Articles & blog posts
|
|
|
|
* https://in-kotlin.com/design-patterns/singleton[In Kotlin, Singleton class / Object class / Companion object] |