Skip to content

Commit

Permalink
SONARJAVA-4460 Improve RSPEC to show the most recent recommended Sing…
Browse files Browse the repository at this point in the history
…leton implementations first (#4560)
  • Loading branch information
irina-batinic-sonarsource authored Nov 21, 2023
1 parent 751ab82 commit 5b937f1
Showing 1 changed file with 71 additions and 72 deletions.
Original file line number Diff line number Diff line change
@@ -1,16 +1,16 @@
<p>The Singleton design pattern is a creational pattern. It ensures that only one instance of a class is created and provides a global point of access
to it. In Java, there are several ways to implement a Singleton, and the debate about the best approach has yet to be settled.</p>
<p>This rule marks all classes that are considered as Singletons, regardless of how they are implemented. This helps developers identifying where in
the code Singletons are used. Singletons should be reviewed to confirm whether or not a Singleton is truly necessary and whether the selected
implementation is the most suitable for the context.</p>
<p>Every Singleton implementation has its advantages and disadvantages. This rule exists to bring attention to them, so that informed decisions can be
<p>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.</p>
<p>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.</p>
<p>Every Singleton implementation has its advantages and disadvantages. This rule exists to bring attention to them so that informed decisions can be
made.</p>
<h2>Why is this an issue?</h2>
<p>While the Singleton pattern can be useful in certain situations, overusing it can have several drawbacks:</p>
<ul>
<li> Tight coupling: The Singleton pattern can create tight coupling between the Singleton class and other classes that use it, which can make the
code difficult to maintain and modify. </li>
<li> Global state: The Singleton pattern can create global state, which can make it difficult to manage the state of the application and can lead to
<li> 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. </li>
<li> 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. </li>
<li> 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. </li>
Expand All @@ -22,38 +22,59 @@ <h2>Why is this an issue?</h2>
<p>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.</p>
<h3>What is the potential impact?</h3>
<h4>Public Static Field Implementation</h4>
<h4>Enum Implementation</h4>
<pre>
public class PublicStaticSingleton {
public enum EnumSingleton {

public static final PublicStaticSingleton INSTANCE = new PublicStaticSingleton();
INSTANCE;

private PublicStaticSingleton() {}
private EnumSingleton() {
// Initialization code here...
}
}
</pre>
<p><strong>Advantage</strong>:</p>
<p><strong>Advantages</strong>:</p>
<p>This implementation is thread-safe by default because the initialization of an Enum value is guaranteed to be thread-safe and atomic.</p>
<p>The Enum Singleton implementation allows for lazy initialization while also providing thread-safety guarantees.</p>
<h4>Bill Pugh Implementation</h4>
<pre>
public class BillPughSingleton {

private BillPughSingleton(){}

private static class SingletonHelper {
private static final BillPughSingleton INSTANCE = new BillPughSingleton();
}

public static BillPughSingleton getInstance() {
return SingletonHelper.INSTANCE;
}
}
</pre>
<p><strong>Advantages</strong>:</p>
<p>The instance is created only at the first call of the <code>getInstance()</code> method.</p>
<p>This implementation is thread-safe.</p>
<p><strong>Disadvantage</strong>:</p>
<p>This implementation does not allow lazy initialization: the constructor runs as soon as the class is initialized.</p>
<h4>Eager Initialization Implementation</h4>
<h4>Thread Safe Implementation</h4>
<pre>
public class EagerInitializedSingleton {
public class ThreadSafeSingleton {

private static final EagerInitializedSingleton instance = new EagerInitializedSingleton();
private static ThreadSafeSingleton instance;

private EagerInitializedSingleton() {}
private ThreadSafeSingleton(){}

public static EagerInitializedSingleton getInstance() {
public static synchronized ThreadSafeSingleton getInstance() {
if (instance == null) {
instance = new ThreadSafeSingleton();
}
return instance;
}
}
</pre>
<p><strong>Advantage</strong>:</p>
<p>This implementation is thread-safe, as the instance variable is initialized when the class is loaded.</p>
<p><strong>Disadvantages</strong>:</p>
<p>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.</p>
<p>This implementation doesn’t provide any options for exception handling.</p>
<p>This implementation is thread-safe.</p>
<p><strong>Disadvantage</strong>:</p>
<p>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.</p>
<h4>Static Block Initialization Implementation</h4>
<pre>
public class StaticBlockSingleton {
Expand All @@ -79,6 +100,25 @@ <h4>Static Block Initialization Implementation</h4>
<p>Compared to the Eager Initialization, this implementation provides options for exception handling.</p>
<p><strong>Disadvantage</strong>:</p>
<p>The instance is created even if it’s never used, like for the Eager Initialization implementation.</p>
<h4>Eager Initialization Implementation</h4>
<pre>
public class EagerInitializedSingleton {

private static final EagerInitializedSingleton instance = new EagerInitializedSingleton();

private EagerInitializedSingleton() {}

public static EagerInitializedSingleton getInstance() {
return instance;
}
}
</pre>
<p><strong>Advantage</strong>:</p>
<p>This implementation is thread-safe, as the instance variable is initialized when the class is loaded.</p>
<p><strong>Disadvantages</strong>:</p>
<p>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.</p>
<p>This implementation doesn’t provide any options for exception handling.</p>
<h4>Lazy Initialization Implementation</h4>
<pre>
public class LazyInitializedSingleton {
Expand All @@ -99,58 +139,17 @@ <h4>Lazy Initialization Implementation</h4>
<p>This implementation works fine in the case of the single-threaded environment.</p>
<p><strong>Disadvantage</strong>:</p>
<p>This implementation is not thread-safe if multiple threads are at the same time in the <code>if</code> condition.</p>
<h4>Thread Safe Implementation</h4>
<h4>Public Static Field Implementation</h4>
<pre>
public class ThreadSafeSingleton {

private static ThreadSafeSingleton instance;
public class PublicStaticSingleton {

private ThreadSafeSingleton(){}
public static final PublicStaticSingleton INSTANCE = new PublicStaticSingleton();

public static synchronized ThreadSafeSingleton getInstance() {
if (instance == null) {
instance = new ThreadSafeSingleton();
}
return instance;
}
private PublicStaticSingleton() {}
}
</pre>
<p><strong>Advantage</strong>:</p>
<p>This implementation is thread-safe.</p>
<p><strong>Disadvantage</strong>:</p>
<p>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.</p>
<h4>Bill Pugh Implementation</h4>
<pre>
public class BillPughSingleton {

private BillPughSingleton(){}

private static class SingletonHelper {
private static final BillPughSingleton INSTANCE = new BillPughSingleton();
}

public static BillPughSingleton getInstance() {
return SingletonHelper.INSTANCE;
}
}
</pre>
<p><strong>Advantages</strong>:</p>
<p>The instance is created only at the first call of the <code>getInstance()</code> method.</p>
<p>This implementation is thread-safe.</p>
<h4>Enum Implementation</h4>
<pre>
public enum EnumSingleton {

INSTANCE;

private EnumSingleton() {
// Initialization code here...
}
}
</pre>
<p><strong>Advantages</strong>:</p>
<p>This implementation is thread-safe by default because the initialization of an Enum value is guaranteed to be thread-safe and atomic.</p>
<p>The Enum Singleton implementation allows for lazy initialization while also providing thread-safety guarantees.</p>
<h2>How to fix it</h2>
<p>This implementation does not allow lazy initialization: the constructor runs as soon as the class is initialized.</p>

0 comments on commit 5b937f1

Please sign in to comment.