A design pattern is a general reusable solution to the commonly occurring problems within a given context. Programmers often encounter the same problem repeatedly. Instead of everyone solving in their own way, the industry has come up with a best-practice solution that has been documented and proven to work.
Design patterns are divided into following categories
- Creational Design Patterns
- Singleton Pattern
- Factory Pattern
- Abstract Factory Pattern
- Builder Pattern
- Prototype Pattern
- Structural Design Patterns
- Adapter Pattern
- Composite Pattern
- Proxy Pattern
- Flyweight Pattern
- Facade Pattern
- Bridge Pattern
- Decorator Pattern
- Behavioral Design Patterns
- Template Method Pattern
- Mediator Pattern
- Chain of Responsibilty Pattern
- Observer Pattern
- Strategy Pattern
- Command Pattern
- State Pattern
- Visitor Pattern
- Interpreter Pattern
- Iterator Pattern
- Memento Pattern
- Microservice Design patterns
- Strangler Pattern
- Bulkhead Pattern
- Sidecar Pattern
- API gateway Pattern
- Aggregator Pattern
- Proxy Pattern
- Gateway Routing Pattern
- Chained Microservice Pattern
- Branch Pattern
- Client-Side UI Composition Pattern
- Database per Service
- Shared Database per Service
- Command Query Responsibility Segregation (CQRS)
- Event Sourcing
- Saga Pattern
- Log Aggregation
- Performance Metrics
- Distributed Tracing
- Health Check
- External Configuration
- Service Discovery Pattern
- Circuit Breaker Pattern
- Blue-Green Deployment Pattern
I will be covering some of the design patterns in this Blog post which are important and are useful in day-to-day programming.
Singleton Design Pattern
Ensure the class has only a single instance and provides a global point of access to it, However, this is easy said than done.
Different ways you can break singleton pattern
- Reflection
- Serialization
- Clone
- multi-threaded access
- multiple class loaders : No defence possible
Eager-Singleton
There are different ways singleton can be implemented. The first way to implement is via eagerly initialize the class and make the construct private. Singleton can be broken by Serialization, and Reflection, the following code describes the ways the singleton is broken and defend against it.
package com.jonesjalapat.classloader;
class EagerSingleton implements Serializable{
private static EagerSingleton eagerSingleton = new EagerSingleton();
private EagerSingleton() {
//defend against reflection
// if(eagerSingleton != null) {
// throw new RuntimeException("Constructor cannot be called directly");
// }
System.out.println(" Creating EagerSingleton ....");
}
public static EagerSingleton getInstance() {
return eagerSingleton;
}
//defend against serialization
// private Object readResolve() throws ObjectStreamException {
// System.out.println("return the created Instance ");
// return eagerSingleton;
// }
}
public class TestEagerSingleton {
public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException, NoSuchMethodException, SecurityException, FileNotFoundException, IOException, ClassNotFoundException {
EagerSingleton egs1 = EagerSingleton.getInstance();
EagerSingleton egs2 = EagerSingleton.getInstance();
System.out.println("EagerSingleton1 " + egs1.hashCode());
System.out.println("EagerSingleton2 " + egs2.hashCode());
// Break Singleton using Reflection
createObjectUsingReflection();
// Break Singleton using Serialization
createObjectUsingSerialization(egs2);
}
private static void createObjectUsingSerialization(EagerSingleton egs2) throws FileNotFoundException, IOException, ClassNotFoundException {
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("eg3.ser"));
oos.writeObject(egs2);
ObjectInputStream ois = new ObjectInputStream(new FileInputStream("eg3.ser"));
EagerSingleton egs3 = (EagerSingleton)ois.readObject();
//Tid Bit : In serialization Objects are called without invoking Constructor
System.out.println("EagerSingleton from Serialization " + egs3.hashCode());
}
private static void createObjectUsingReflection() throws ClassNotFoundException, InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException, NoSuchMethodException, SecurityException {
Class clazz = Class.forName("com.jonesjalapat.classloader.EagerSingleton");
Constructor<EagerSingleton> constructor = clazz.getDeclaredConstructor();
constructor.setAccessible(true);
EagerSingleton eagerSingleton = constructor.newInstance();
System.out.println("EagerSingleton from Reflection " + eagerSingleton.hashCode());
}
}
We use hashcode equality to check whether a new instance was created or not
You can view the code here https://github.com/jonesjalapatgithub/jonesjalapatblog/blob/main/src/com/jones/designPattern/TestEagerSingleton.java
Lazy-Singleton
We don’t want to initialize Singleton unless it’s needed, hence we try to initialize Singleton when it is needed. The singleton is created only when the instance is needed.
Double checked pattern : Reasons why we used Double checked pattern
* Synchronization is needed since multiple threads create problem without it.
* Synchronized methods are not good since there is a performance degradation of factor of 100 or higher,
* Henceforth a double checked pattern used to do synchronization only at objet creation.
* Double checked has a problem of partially constructed object, henceforth we use a volatile variable approach
* Direct ref to volatile variable gives a performance hit due to not using register level caches, hence
* A local ref is returned which gives us atleast 40% performance boost.
* There are other approaches as-well like using varHandle of java 9, but for now this is enough.
class LazySingleton implements Cloneable{
private static volatile LazySingleton lazySingleton;
private LazySingleton() {
System.out.println(" Creating EagerSingleton ....");
}
//Defend against multiple threads
public static LazySingleton getInstance() {
LazySingleton lazySingletonRef = lazySingleton;
if(lazySingletonRef == null) {
synchronized(LazySingleton.class) {
lazySingletonRef = lazySingleton;
if (lazySingletonRef == null) {
lazySingletonRef = lazySingleton = new LazySingleton();
}
}
}
return lazySingletonRef;
}
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
}
public class TestLazySingleton {
static void call() {
LazySingleton lzs4 = LazySingleton.getInstance();
System.out.println("LazySingleton from Multithread " + lzs4.hashCode());
}
public static void main(String[] args) throws CloneNotSupportedException {
LazySingleton lzs1 = LazySingleton.getInstance();
LazySingleton lzs2 = LazySingleton.getInstance();
System.out.println("LazySingleton1 " + lzs1.hashCode());
System.out.println("LazySingleton2 " + lzs2.hashCode());
// Break Singleton using Cloning
createObjectUsingCloning(lzs2);
// Trying to break Singleton using
createObjectUsingMultipleThreads(lzs2);
}
// Not broken since we use a double checked pattern
private static void createObjectUsingMultipleThreads(LazySingleton lzs2) {
ExecutorService service = Executors.newFixedThreadPool(2);
service.submit(TestLazySingleton::call);
service.submit(TestLazySingleton::call);
service.shutdown();
}
private static void createObjectUsingCloning(LazySingleton lzs2) throws CloneNotSupportedException {
LazySingleton lzs3 = (LazySingleton) lzs2.clone();
System.out.println("LazySingleton from Reflection " + lzs3.hashCode());
}
}
You can find Code here : https://github.com/jonesjalapatgithub/jonesjalapatblog/blob/main/src/com/jones/designPattern/TestLazySingleton.java
Enum-Singleton
The best way to create SIngleton is using Enum since defense against Serialization, Reflection, Cloning, and Multi-thread is present by default.
public enum TestEnumSingleton {
INSTANCE;
private Set<String> availableSeats;
private TestEnumSingleton() {
availableSeats = new HashSet<String>();
availableSeats.add("1");
availableSeats.add("2");
availableSeats.add("3");
}
public boolean bookSeat(String seat) {
return availableSeats.remove(seat);
}
static void call() {
TestEnumSingleton es1 = TestEnumSingleton.INSTANCE;
System.out.println("EnumSingleton from Multithread " + es1.hashCode());
}
public static void main(String[] args) {
ExecutorService service = Executors.newFixedThreadPool(2);
service.submit(TestEnumSingleton::call);
service.submit(TestEnumSingleton::call);
TestEnumSingleton es2 = TestEnumSingleton.INSTANCE;
System.out.println("EnumSingleton " + es2.hashCode() + " booked success :" + es2.bookSeat("1"));
}
}
Builder Pattern
A builder pattern helps us to create complex objects with many parameters in a step-by-step easier way. In the below example We will create an immutable class using a Builder pattern, basically, an immutable class should have all its object’s values as immutable.
package com.jones.designPattern;
import java.util.HashMap;
public class ImmutableClass {
//required fields
private int id;
private StringBuilder name;
private HashMap<String, String> properties;
private ImmutableClass(ImmutableClassBuilder builder) {
this.id = builder.id;
this.name = builder.name;
this.properties = builder.properties;
}
public static class ImmutableClassBuilder {
private int id;
private StringBuilder name;
private HashMap<String, String> properties;
public ImmutableClassBuilder(int id) {
this.id = id;
}
@SuppressWarnings("unchecked")
public ImmutableClassBuilder setProperties(HashMap<String,String> hashMap){
this.properties = (HashMap<String, String>) hashMap.clone();
return this;
}
public ImmutableClassBuilder setName(String name){
this.name = new StringBuilder(name);
return this;
}
public ImmutableClass build(){
return new ImmutableClass(this);
}
}
@Override
public String toString() {
return "ImmutableClass created with "
+ " name: " + this.name
+ " hashCode: " + this.hashCode();
}
public static void main(String[] args) {
HashMap<String, String> properties = new HashMap<>();
ImmutableClass immutableClass = new ImmutableClass.ImmutableClassBuilder(1)
.setProperties(properties)
.setName("testBuilder")
.build();
System.out.println(immutableClass);
}
}
There is certainly a great deal to learn about this topic. I like all of the points you made.