-
SingletonModeling/DesignPattern 2020. 2. 24. 14:05
1. Overview
The singleton pattern is one of the simplest design patterns. Sometimes we need to have only one instance of our class, for example, a single DB connection shared by multiple objects as creating a separate DB connection for every object may be costly. Similarly, there can be a single configuration manager or error manager in an application that handles all problems instead of creating multiple managers.
2. Eager
public class EagerRegistry { private EagerRegistry() {} private static final EagerRegistry INSTANCE = new EagerRegistry(); public static EagerRegistry getInstance() { return INSTANCE; } }
2.1 Limitation
2.1.1 reflection
2.1.2 serialization
With serialization is when you serialized an object the JVM doesn't really care that your constructor is private it's still going to construct an object anyway.
class BasicSingleton implements Serializable { private BasicSingleton() {} private static final BasicSingleton INSTANCE = new BasicSingleton(); public static BasicSingleton getInstance() { return INSTANCE; } private int value = 0; public int getValue() { return value; } public void setValue(int value) { this.value = value; } } public class Limitation { static void saveToFIle(BasicSingleton singleton, String filename) throws Exception { try(FileOutputStream fileOut = new FileOutputStream(filename); ObjectOutputStream out = new ObjectOutputStream(fileOut)) { out.writeObject(singleton); } } static BasicSingleton readFromFile(String filename) throws Exception { try(FileInputStream fileIn = new FileInputStream(filename); ObjectInput in = new ObjectInputStream(fileIn)) { return (BasicSingleton) in.readObject(); } } public static void main(String[] args) throws Exception{ // 1. reflection // 2. serialization BasicSingleton singleton = BasicSingleton.getInstance(); singleton.setValue(111); String filename = "singleton.bin"; saveToFIle(singleton, filename); singleton.setValue(222); BasicSingleton singleton2 = readFromFile(filename); System.out.println(singleton == singleton2); System.out.println(singleton.getValue()); System.out.println(singleton2.getValue()); } } // output false 222 111
2.2 Resolve Limitation
In actual fact now it is possible to very easily fix this situation and this is fixed by providing the read resolve property or in actual fact the read result method inside the Serializable object and just specifying the instance that you want to resolve into.
So now we're giving the JVM a hint that whenever serialization happens it has to happen in the context of this instance it has to happen into the instance as opposed to making your object.
class BasicSingleton implements Serializable { ... protected Object readResolve() { return INSTANCE; } } // output true 222 222
2.3 Static Block Singleton
2.3.1 Intuition
If you currently have exceptions inside of the singleton constructor you are in real trouble. But if you use a static blog then things are a bit different.
... private StaticBlockSingleton() throws IOException { System.out.println("Singleton is initializing"); File.createTempFile(".", "."); } ...
So in this example is the fact that if you do have a singleton constructor which can actually throw something then you have to slightly change your approach so instead of just declaring a variable and exposing it through the get insense what you would make it typically is a static block away you put in some slow try-catch and you try making the instance of the singleton and you catch some exception and handle that.
2.3.2 Handling Exception
class StaticBlockSingleton{ private StaticBlockSingleton() throws IOException { System.out.println("Singleton is initializing"); File.createTempFile(".", "."); } private static StaticBlockSingleton instance; static { try { instance = new StaticBlockSingleton(); } catch (Exception e) { System.err.println("failed to create singleton"); } } }
3. Lazy
3.1 synchronized
This has an obvious performance implication.
class LazySingleton { private static LazySingleton instance; private LazySingleton() { System.out.println("initializing a lazy singleton"); } public static synchronized LazySingleton getInstance() { if(instance == null) { instance = new LazySingleton(); } return instance; } }
3.2 Double-check
public class LazyRegistryWithDCL { private LazyRegistryWithDCL() { } // volatile overcome cache value of variable in a one of the register private static volatile LazyRegistryWithDCL INSTANCE; public static LazyRegistryWithDCL getInstance() { if(INSTANCE == null) { synchronized (LazyRegistryWithDCL.class) { // Double check if(INSTANCE == null) { INSTANCE = new LazyRegistryWithDCL(); } } } return INSTANCE; } }
3.3 Inner Static Singleton
class InnerStaticSingleton { private InnerStaticSingleton() {} private static class Impl { private static final InnerStaticSingleton INSTANCE = new InnerStaticSingleton(); } public InnerStaticSingleton getInstrance() { return Impl.INSTANCE; } }
3.4 Enum Based Singleton
There is no way of inheriting but apart from that you can actually use this approach provided that you don't need any state to be persisted. An enum already has a private default constructor and if you don't add a constructor here it's going to be a problem people are not going to be able to make new instances often in any way. But you can make a constructor you don't have to even add the private keyword here you could just make a constructor called a base singleton. It's always private essentially. They're implicitly Serializable you don't have to you know implement Serializable and whatever you have this problem that the fields are not serialized the only thing that's serialized is the name of the instance. That's all that is serialized.
even though you can use an enum with a single value of an instance to implement a singleton. The problem is you cannot serialize it effectively because all the fields don't get serialized. And in addition, the problem is you cannot inherit from this ENM because it's an income basically so you cannot inherit from it.
enum EnumBasedSingleton { INSTANCE; EnumBasedSingleton() { value = 42; } private int value; public int getValue() { return value; } public void setValue(int value) { this.value = value; } } public class EnumBasedSingletonDemo { static void saveToFIle(EnumBasedSingleton singleton, String filename) throws Exception { try(FileOutputStream fileOut = new FileOutputStream(filename); ObjectOutputStream out = new ObjectOutputStream(fileOut)) { out.writeObject(singleton); } } static EnumBasedSingleton readFromFile(String filename) throws Exception { try(FileInputStream fileIn = new FileInputStream(filename); ObjectInput in = new ObjectInputStream(fileIn)) { return (EnumBasedSingleton) in.readObject(); } } public static void main(String[] args) throws Exception { String filename = "myfile.bin"; EnumBasedSingleton singleton = EnumBasedSingleton.INSTANCE; singleton.setValue(111); saveToFIle(singleton, filename); EnumBasedSingleton singleton2 = readFromFile(filename); System.out.println(singleton2.getValue()); } }
3.5 Monostate
class ChiefExecutiveOfficer { private static String name; private static int age; public String getName() { return name; } public void setName(String name) { ChiefExecutiveOfficer.name = name; } public int getAge() { return age; } public void setAge(int age) { ChiefExecutiveOfficer.age = age; } @Override public String toString() { return "CheifExecutiveOfficer{" + "name='"+name+'\''+", age=" + age + '}'; } }
3.6 Multiton
Instead what the Maltese on tries to do is that there is a finite set of instances and for every single instance you can also implement additional benefits such as lazy loading for example. But what we really want to do is we want to somehow restrict the number of instances that are created or maybe have those instances associated with some sort of enumeration for example.
enum Subsystem { PRIMARY, AUXILIARY, FALLBACK } class Printer { private Printer() {} private static HashMap<Subsystem, Printer> instances = new HashMap<>(); public static Printer get(Subsystem ss) { if (instances.containsKey(ss)) return instances.get(ss); Printer instance = new Printer(); instances.put(ss, instance); return instance; } }
3. Reference
'Modeling > DesignPattern' 카테고리의 다른 글
Observer (0) 2020.02.24 Builder (0) 2020.02.24 Model–view–controller (MVC) (0) 2020.02.23 Categorizing Design Pattern (0) 2019.09.29 Abstract factory (0) 2019.08.23