rspec/rules/S6548/java/rule.adoc

204 lines
6.0 KiB
Plaintext
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

The Singleton design pattern is a creational pattern.
It ensures that only one class instance is created and provides a global point of access to it.
There are several ways to implement a Singleton in Java, and the debate about the best approach has yet to be settled.
This rule marks all classes that are considered as Singletons, regardless of how they are implemented.
This helps developers identify where in the code Singletons are used.
Singletons should be reviewed to confirm whether a Singleton is truly necessary and whether the selected implementation is the most suitable for the context.
Every Singleton implementation has its advantages and disadvantages.
This rule exists to bring attention to them so that informed decisions can be made.
== Why is this an issue?
While the Singleton pattern can be useful in certain situations, overusing it can have several drawbacks:
* Tight coupling: The Singleton pattern can create tight coupling between the Singleton class and other classes that use it, making the code difficult to maintain and modify.
* Global state: The Singleton pattern can create a global state, making it difficult to manage the state of the application and leading to unexpected behavior.
* Testing: The Singleton pattern can make it difficult to test classes that depend on the Singleton, as the Singleton cannot be easily substituted with a mock object.
* Scalability: The Singleton pattern can make it difficult to scale an application, as it can create a bottleneck if multiple threads try to access the Singleton concurrently.
* Dependency injection: The Singleton pattern can make it difficult to use dependency injection frameworks, as the Singleton instance is usually created statically.
In general, the Singleton pattern should be used sparingly and only in situations where it provides a clear benefit over other patterns or approaches.
It is important to consider the drawbacks and tradeoffs of using the Singleton pattern before incorporating it into an application.
=== What is the potential impact?
==== Enum Implementation
[source,java]
----
public enum EnumSingleton {
INSTANCE;
private EnumSingleton() {
// Initialization code here...
}
}
----
*Advantages*:
This implementation is thread-safe by default because the initialization of an Enum value is guaranteed to be thread-safe and atomic.
The Enum Singleton implementation allows for lazy initialization while also providing thread-safety guarantees.
==== Bill Pugh Implementation
[source,java]
----
public class BillPughSingleton {
private BillPughSingleton(){}
private static class SingletonHelper {
private static final BillPughSingleton INSTANCE = new BillPughSingleton();
}
public static BillPughSingleton getInstance() {
return SingletonHelper.INSTANCE;
}
}
----
*Advantages*:
The instance is created only at the first call of the `getInstance()` method.
This implementation is thread-safe.
==== Thread Safe Implementation
[source,java]
----
public class ThreadSafeSingleton {
private static ThreadSafeSingleton instance;
private ThreadSafeSingleton(){}
public static synchronized ThreadSafeSingleton getInstance() {
if (instance == null) {
instance = new ThreadSafeSingleton();
}
return instance;
}
}
----
*Advantage*:
This implementation is thread-safe.
*Disadvantage*:
It reduces the performance because of the cost associated with the synchronized method.
To avoid this extra overhead every time, double-checked locking principle should be used.
==== Static Block Initialization Implementation
[source,java]
----
public class StaticBlockSingleton {
private static StaticBlockSingleton instance;
private StaticBlockSingleton(){}
static {
try {
instance = new StaticBlockSingleton();
} catch (Exception e) {
throw new RuntimeException("Exception while creating singleton instance");
}
}
public static StaticBlockSingleton getInstance() {
return instance;
}
}
----
*Advantage*:
Compared to the Eager Initialization, this implementation provides options for exception handling.
*Disadvantage*:
The instance is created even if it's never used, like for the Eager Initialization implementation.
==== Eager Initialization Implementation
[source,java]
----
public class EagerInitializedSingleton {
private static final EagerInitializedSingleton instance = new EagerInitializedSingleton();
private EagerInitializedSingleton() {}
public static EagerInitializedSingleton getInstance() {
return instance;
}
}
----
*Advantage*:
This implementation is thread-safe, as the instance variable is initialized when the class is loaded.
*Disadvantages*:
The instance is created even if it's never used, which can be wasteful in terms of memory usage.
However, if the Singleton is expected to be used frequently or is not too memory-intensive, Eager Initialization can be a good choice.
This implementation doesnt provide any options for exception handling.
==== Lazy Initialization Implementation
[source,java]
----
public class LazyInitializedSingleton {
private static LazyInitializedSingleton instance;
private LazyInitializedSingleton(){}
public static LazyInitializedSingleton getInstance() {
if (instance == null) {
instance = new LazyInitializedSingleton();
}
return instance;
}
}
----
*Advantage*:
This implementation works fine in the case of the single-threaded environment.
*Disadvantage*:
This implementation is not thread-safe if multiple threads are at the same time in the `if` condition.
==== Public Static Field Implementation
[source,java]
----
public class PublicStaticSingleton {
public static final PublicStaticSingleton INSTANCE = new PublicStaticSingleton();
private PublicStaticSingleton() {}
}
----
*Advantage*:
This implementation is thread-safe.
*Disadvantage*:
This implementation does not allow lazy initialization: the constructor runs as soon as the class is initialized.