3 Design Patterns I Applied in My Java Code in One Hour

DN Tech
Geek Culture
Published in
4 min readNov 28, 2021
https://techvidvan.com/tutorials/java-design-patterns/

Design patterns are crucial for writing quality code. Especially in object-oriented languages such as Java and Python, applying design patterns appropriately improves scalability, security and readability. Yesterday, when I was developing a data cache service application, there were 3 design patterns used to draft an intial framework for future development.

Factory Pattern

https://refactoring.guru/design-patterns/factory-method

In my case, the data cache service should be able to take in different cache clients such as Jedis (Java client for Redis) or GemFire client. By creating a factory, depending on the configuration, we will create needed cache client:

public class ClientCacheFactory {
private final CacheOrderService cacheOrderService;

public static CacheOrderService create(CacheClientConfig config){
// If config is Redis-related
// return new RedisCacheOrderService();
// If config is GemFire-related
// return new GemFireCacheOrderService();
}
}

Above code hands over the instantiation of client cache service to the factory class instead of calling each one in the main thread. Notice that the CacheOrderService returned is actually an interface class which RedisCacheOrderService.java and GemFireCacheOrderService.java implement.

//InstantiationCacheClientConfig config = new CacheClientConfig();
ClientCacheFactory.create(config)

As it well encapsulates the class creation, this improves the scalability of the code such that if there are more cache clients to be added, only the factory class needs to be changed.

Singleton Pattern

https://refactoring.guru/design-patterns/singleton

We would like our application to run with only one copy of the cache client given any time. This is where Singleton pattern comes in handy.

public class ClientCacheFactory {
private static CacheOrderService cacheOrderService;

public static CacheOrderService create(CacheClientConfig config){
if (cacheOrderSerive != null) {
return this.cacheOrderService;
}
// create a new cacheOrderService and assign it
}
}

Note that we declared CacheOrderService class as the first statement. This helps us identify if any instance of this object has been created. Whenever the factory wants to create a CacheOrderService, it will check if the cacheOrderService within the factory class has been assigned yet. If it is null, it will create a new one based on our conditions. If not, it will return the existing cacheOrderService. The static keyword makes sure this instance is sustained throughout all the instances of the factory class as well. But in the first place, we could make ClientCacheFactory as a static class to avoid instances of it.

Builder Pattern

https://refactoring.guru/design-patterns/builder

Builder pattern provides better control of construction of an object. There is a data transfer object in my project called OrderDetails which consists of a few fields such as orderId, creationTime and payload (simplified for demonstration).

public class OrderDetails {

private final String orderId;
private final String creationTime;
private final Object payload;

public OrderDetails(){};

public static class Builder {
private final String orderId;
private String creationTime;
private Object payload;

public Builder (String orderId) {
this.orderId = orderId;
}
public Builder atCreationTime(String creationTime){
this.creationTime = creationTime;
return this;
}
public Builder withPayload(String payload){
this.payload = payload;
}

public OrderDetails build(){
final OrderDetails orderDetails = new OrderDetails();
orderDetails.orderId = this.orderId;
orderDetails.creationTime = this.creationTime;
orderDetails.payload = this.payload;
return orderDetails;
}
}
}

In this OrderDetails class, we first have private fields and a constructor. Then we have an inner class called Builder so that we will use this Builder class to construct all the parameters. Below code shows how we can construct this object using builder.

final OrderDetails orderDetails = new OrderDetails.Builder(12345)
.atCreationTime('20211128')
.withPayload('abcd')
.build()

This increases the readability of the code so that you don’t have to add all the set statements in the conventional way. Furthermore, without setters, this ensures immutability of the object created.

If you would like to explore more design patterns, the link below the pictures above is quite useful as it illustrates common design patterns in an easier way to understand.

Inspiration

When we talk about programming, many of us just jump into typing on the keyboard directly. Merely typing busy on the keyboard isn’t programming. It is coding. Programming is about having a thinking pattern, or a framework or achitecture of solving a problem and apply those into coding. This leads to a win-win situation where you yourself as a programmer will not go into a state of feeling lost and confused by your own code and your client is able to scale, expand or extract based on your applications in the future.

I hope this article is useful to you. If you are like me who’s aspring to learn something related to technology, or reflect on work and life on a regular basis, follow my channel and stay updated to my little inspirations and summaries from my daily work and life.

--

--

DN Tech
Geek Culture

Backend Software Engineer who shares about my daily work bits