Java Patterns Worth Knowing
Design patterns describe the best practises and strategies to follow when trying to solve a generic design problem. These patterns were created by experienced object-oriented programmers to address commonly occurring problems and have been applied countless times in all sorts of contexts and situations proving effectively their value. Furthermore, it is worth pointing out that being these patterns language-independent they can be used in any Object Oriented programming language.Design patterns are classified and grouped in four different categories which are:
- Creational patters (delegation): These patterns offer the flexibility to decide who is responsible for the object creation and how it will be created, which object will be created and when the creation will take place. Basically, these patterns provide an abstract way to define the object instantiation:
- Structural patters (aggregation): Focus upon the composition of classes and objects to build larger structures and ease the identification of relationships between entities.
- Composite
- Decorator
- Proxy
- Facade
- Behavioural patters (consultation): Concern the communication between objects increasing the flexibility in controlling how this communication is performed.
- Mediator
- Chain of Responsibility
- Observer
- State
- Strategy
- Architectural design patters (architecture level): Patterns related to the different application tiers.
- Model-View-Controller
- Service-Oriented architecture
- Concurrency patterns: Define a way to deal with the multi-threader programming paradigm.
- Double-checked locked'
- Monitor object
- Thread pool
- Join
- Lock
Lastly before we delve into the various patterns, I would like to clarify that designs patterns are not ready-to-use solutions as in libraries that you could use in your program but rather serve as a template solution which needs to be prepared and accommodated to a given context.
Factory Pattern
As the name itself indicates, a factory is used to produce multiple instances of certain type of object(s) on demand. Further, the main responsibility of a factory is to decide which class(es) will be instantiated to create the required object(s) and how the creation of the object(s) will be performed.
Let's consider the following example - a pastry factory (PastryFactory) should be able to produce various kinds of specific baked products - ConcreteBakedProducts (palmiers, biscuits, profiterole, etc). However, the bakery (BakeryClient) which is the client of the factory, might not want to know about how the pastries are implemented (created) but rather just order them and receive a concrete instantiation of the product requested in the form of its interface - Pastry. The following figure shows the UML diagram of the example described previously:
package com.creacodetive.patterns.factory.model;
/**
* Interface for Pastry products
*
* @author dikhanr
*
*/
public interface Pastry {
public String pastryName();
public String description();
public String details();
}
As you will see further down, the PastryFactory does not return concrete implementations of pastries such as Palmiers or Biscuits but the actual interface. The createNewPastry(..) method analyses the requested pastryType and returns a Pastry object to the client. Please note that even though we are returning Pastry object instances, this does not mean that we no longer have the concrete implementation (biscuit, palmier). The concrete instance is under-covered in the Pastry object returned.
package com.creacodetive.patterns.utils;
import com.creacodetive.patterns.factory.model.Biscuit;
import com.creacodetive.patterns.factory.model.Palmier;
import com.creacodetive.patterns.factory.model.Pastry;
/**
* Factory in charge of producing the different instances of pastries
*
* @author Daniel Isaac Khan Ramiro
*
*/
public class PastryFactory {
public static Pastry createNewPastry(String pastryType)
{
Pastry pastry = null;
switch(pastryType.toUpperCase()){
case "PALMIER":
pastry = new Palmier();
break;
case "BISCUIT":
pastry = new Biscuit();
break;
}
return pastry;
}
}
Thanks to the aforementioned abstraction, the Bakery can easily displayStock() by just calling methods that are part of the contract between the Pastry interface and the concrete classes - pastry.details().
package com.creacodetive.patterns.factory.api;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import com.creacodetive.patterns.factory.model.Pastry;
import com.creacodetive.patterns.utils.PastryFactory;
/**
* Client that would use the factory to get concrete instances of Pastry
*
* @author Daniel Isaac Khan Ramiro
*
*/
public class BakeryClient {
private List<pastry> pastriesList;
public BakeryClient()
{
setPastriesList(new ArrayList<pastry>());
}
public void orderNewPastry(String pastryType)
{
Pastry pastry = PastryFactory.createNewPastry(pastryType);
if(pastry != null)
pastriesList.add(pastry);
}
public void displayStock()
{
Iterator<pastry> itr = pastriesList.iterator();
while(itr.hasNext()) {
Pastry pastry = itr.next();
System.out.println(pastry.details());
}
}
public List<pastry> getPastriesList() {
return pastriesList;
}
public void setPastriesList(List<pastry> pastriesList) {
this.pastriesList = pastriesList;
}
}
Finally, just to make sure that the program behaves as expected, let's run the following simple test client and confirm that the factory generates sweetie and tasty palmiers and biscuits.
package com.creacodetive.patterns.factory.api;
public class Test {
public static void main(String[] args)
{
BakeryClient bc = new BakeryClient();
System.out.println("");
System.out.println("Buying a palmier...");
bc.orderNewPastry("palmier");
System.out.println("Displaying Bakery's stock...");
bc.displayStock();
System.out.println("");
System.out.println("Buying a biscuit...");
bc.orderNewPastry("biscuit");
System.out.println("Displaying Bakery's stock...");
bc.displayStock();
}
}
Console Output:
For your reference, here are couple examples of factory patterns used within Java EE APIs:
- java.util.Calendar - getInstance()
- java.sql.Connection interface - createStatement()
- java.rmi.server.RmiClientSocketFactory interface - createSocket()
Singleton Pattern
The main goal of this pattern is to ensure that only one instance of the class is created. This is achieved by providing just one point of access to the class. In order to be able to control this, we need to force the constructor of the class to be private and expose a public method - normally called getInstance() - that will handle the single instantiation of the class internally and return it as requested.
Additionally, in order to come up with a good singleton implementation we need to ensure that issues such as synchronization, serialization and potential JVM issues are fully covered.
Let's now look at how we could solve the synchronization part:
There are many different ways to create a single instance of the class. However, these methods differ from each other in the approach selected when initializing the class. Let's go one by one an analyze them:
- Eager initialization: The instance of the class is created automatically when the class is loaded into the JVM regardless of whether any other class requires it.
Drawback: Object is created in memory even though it may not be needed.
- Lazy initialization: The instantiation of the class is delayed until any other class asks explicitly for an instance of the singleton class. This approach performs better memory wise compare to the previous example as the object will not be created in memory until it is really needed.
Note that the synchronized section wraps the check of (instance == null). Otherwise, if it had been placed right after the check we might have faced synchronization issues. The reason is due to the fact that two threads can potentially arrive at the check point at the same time and then wait for the other thread to finish the instantiation. Thereafter, once the second thread is eligible to enter the synchronized code the class has been already instantiated. Nonetheless, the remaining thread will still instantiate the class again resulting into the creation of two instances. You could also think about having a double check within the synchronized statement that checks for whether the class has been already instantiated. However, in my opinion this would be extra code that is not quite needed as there are other approaches that are cleaner code wise.
- Static block initialization: Considering that static blocks are executing while the class loads but before the constructor of the class is called we can certainly use this in our benefit to create the single instance of the class.
However, as with the first example, the instance of the singleton class will take place before any class explicitly demands it. Therefore, what can we do in order to avoid it? Let's take a peek at the following snipped of code:
As you can see, there are many options which you can choose from. However, I would probably stick with the last approach - Initialization-on-demand holder idiom - as it enables a thread safe and an initialization with great performance.
Having covered all possible initialization methods and also the scenarios where we might face synchronization issues we are now prepared to move to the next step and tackle issues that might arise regarding serialization/de-serialization.
When deserializing an object Java internally always tries to create a new instance of that class. Assuming that our Singleton class implements the Serializable interface, we could ran into an issue when the object is being deserialized if the readResolve() method is not returning the single instance but rather a new instance. Thus, in order to avoid this scenario, we just need to implement the aforementioned method by adding the following code into our Singleton class:
This pattern falls into the 'Behavioural Patters' category enabling the algorithm's behaviour to be selected in runtime. For instance, consider a task that will be in charge of compressing videos into various formats such as .mkv, .avi and .mp4. The family of different algorithms in this case will be the diverse compressing formats that the compressor tool will support.Additionally, in order to come up with a good singleton implementation we need to ensure that issues such as synchronization, serialization and potential JVM issues are fully covered.
Let's now look at how we could solve the synchronization part:
There are many different ways to create a single instance of the class. However, these methods differ from each other in the approach selected when initializing the class. Let's go one by one an analyze them:
- Eager initialization: The instance of the class is created automatically when the class is loaded into the JVM regardless of whether any other class requires it.
package com.creacodetive.patterns.singleton;
public final class EagerInitSingleton {
private static EagerInitSingleton instance = new EagerInitSingleton();
/**
* Note the private access modifier: This forces the client to call the
* getInstance() method in order to create the instance of the class
*/
private EagerInitSingleton() {}
/**
* Method in charge of returning the actual instance of the singleton class.
*
* @return
*/
public static EagerInitSingleton getInstance() {
return instance;
}
}
Drawback: Object is created in memory even though it may not be needed.
- Lazy initialization: The instantiation of the class is delayed until any other class asks explicitly for an instance of the singleton class. This approach performs better memory wise compare to the previous example as the object will not be created in memory until it is really needed.
package com.creacodetive.patterns.singleton;
public final class LazyInitSingleton {
/**
* Declaring the variable volatile to ensure that the reference of the instance
* is not returned until the object is constructed.
*/
private static volatile LazyInitSingleton instance = null;
/**
* Note the private access modifier: This forces the client to call the
* getInstance() method in order to create the instance of the class
*/
private LazyInitSingleton() {}
public static LazyInitSingleton getInstance() {
synchronized (LazyInitSingleton.class) {
if (instance == null)
instance = new LazyInitSingleton();
}
return instance;
}
}
Note that the synchronized section wraps the check of (instance == null). Otherwise, if it had been placed right after the check we might have faced synchronization issues. The reason is due to the fact that two threads can potentially arrive at the check point at the same time and then wait for the other thread to finish the instantiation. Thereafter, once the second thread is eligible to enter the synchronized code the class has been already instantiated. Nonetheless, the remaining thread will still instantiate the class again resulting into the creation of two instances. You could also think about having a double check within the synchronized statement that checks for whether the class has been already instantiated. However, in my opinion this would be extra code that is not quite needed as there are other approaches that are cleaner code wise.
- Static block initialization: Considering that static blocks are executing while the class loads but before the constructor of the class is called we can certainly use this in our benefit to create the single instance of the class.
package com.creacodetive.patterns.singleton;
public class StaticInitSingleton {
private static final StaticInitSingleton instance;
// Static block initialization - executed during the loading of the class and
// even before the constructor is called.
static {
try {
instance = new StaticInitSingleton();
} catch (Exception e) {
throw new RuntimeException("Uffff, i was not expecting this!", e);
}
}
/**
* Note the private access modifier: This forces the client to call the
* getInstance() method in order to create the instance of the class
*/
private StaticInitSingleton() {}
public static StaticInitSingleton getInstance() {
return instance;
}
}
However, as with the first example, the instance of the singleton class will take place before any class explicitly demands it. Therefore, what can we do in order to avoid it? Let's take a peek at the following snipped of code:
package com.creacodetive.patterns.singleton;
public class StaticInnerInitSingleton {
private StaticInnerInitSingleton() {}
private static class InnerLazyInit {
private static final StaticInnerInitSingleton instance = new StaticInnerInitSingleton();
}
public static StaticInnerInitSingleton getInstance() {
return InnerLazyInit.instance;
}
}
As you can see, there are many options which you can choose from. However, I would probably stick with the last approach - Initialization-on-demand holder idiom - as it enables a thread safe and an initialization with great performance.
Having covered all possible initialization methods and also the scenarios where we might face synchronization issues we are now prepared to move to the next step and tackle issues that might arise regarding serialization/de-serialization.
When deserializing an object Java internally always tries to create a new instance of that class. Assuming that our Singleton class implements the Serializable interface, we could ran into an issue when the object is being deserialized if the readResolve() method is not returning the single instance but rather a new instance. Thus, in order to avoid this scenario, we just need to implement the aforementioned method by adding the following code into our Singleton class:
protected Object readResolve() {
return instance;
}
Lastly, we need to let the compiler know that the singleton class implemented is unique thus we will prevent possible exceptions thrown when having the class changing in between the serialization and de-serialization. And, how can we do this? By adding just one line of code, the "Serial Version UId" as follows:
In conclusion, depending upon what kind of demands your application has you might want to consider one approach or the other. However, I would highly recommend that before throwing yourself into the pool and start implementing the singleton pattern you make sure that important aspects such as the initialization, synchronization and serialization are taken into consideration specially when dealing with high performance systems. Having said this and just to be on the safe track with all the above scenarios completely covered, I would recommend using the following snipped when implementing the Singleton Pattern:
A clear example of a real usage of this pattern is SLF4, which stands for "Simple Logging Facade for Java". Having your application using this abstraction for the logging ensures that any other client making use of your framework will be able to plug-in any logging framework (java.util.logging, logback, log4j) that comply with the SLF4J's specification.
In the following pattern we are also making use of this pattern thus stay tuned.
private static final long serialVersionUID = 1L;
In conclusion, depending upon what kind of demands your application has you might want to consider one approach or the other. However, I would highly recommend that before throwing yourself into the pool and start implementing the singleton pattern you make sure that important aspects such as the initialization, synchronization and serialization are taken into consideration specially when dealing with high performance systems. Having said this and just to be on the safe track with all the above scenarios completely covered, I would recommend using the following snipped when implementing the Singleton Pattern:
package com.creacodetive.patterns.singleton;
import java.io.Serializable;
public class SingletonPattern implements Serializable {
private static final long serialVersionUID = -3283340506858300161L;
private SingletonPattern() {}
private static class InnerSingletonHolder {
public static final SingletonPattern instance = new SingletonPattern();
}
public static SingletonPattern getInstance() {
return InnerSingletonHolder.instance;
}
protected Object readResolve() {
return getInstance();
}
}
For your reference, here are couple examples of singleton patterns used within Java EE APIs:- java.lang.Runtime - getRuntime()
- java.awt.Desktop - getDesktop()
Facade Pattern
This pattern tackles the problem that exist when the functionality of a complex system needs to be exposed as a simplified interface to certain client. Encapsulating the complexity of the subsystem in a single interface greatly reduces the learning curve needed to understand the system. Furthermore, it also makes the platform more decoupled providing more independence between the potential clients that might use the interface and the subsystem.
A clear example of a real usage of this pattern is SLF4, which stands for "Simple Logging Facade for Java". Having your application using this abstraction for the logging ensures that any other client making use of your framework will be able to plug-in any logging framework (java.util.logging, logback, log4j) that comply with the SLF4J's specification.
In the following pattern we are also making use of this pattern thus stay tuned.
Strategy Pattern
First we define the interface that will be implemented by the different video formatters.
package com.creacodetive.patterns.strategy;
public interface CompressStrategy {
public void compress(Video file);
}
After having defined the interface the appropriate different compression methods need to be implemented:
package com.creacodetive.patterns.strategy;
public class MKV implements CompressStrategy {
@Override
public void compress(Video file) {
// Compress video using MKV algorithms
}
}
package com.creacodetive.patterns.strategy;
public class MP4 implements CompressStrategy {
@Override
public void compress(Video file) {
// Compress video using MP4 algorithms
}
}
package com.creacodetive.patterns.strategy;
public class AVI implements CompressStrategy {
@Override
public void compress(Video file) {
// Compress video using AVI algorithms
}
}
Finally, in the client class below - VideoCompressor - you will see that there is a method setCompressionStrategy that enables the reconfiguration of the compression strategy to be applied upon the video. This way, we are able to perform different format algorithms on runtime and the client doesn't need to know anything about the actual compression implementations only caring about what the CompressStrategy interface exposes.
package com.creacodetive.patterns.strategy;
public class VideoCompressor {
private CompressionStrategy strategy;
public void setCompressionStrategy(CompressionStrategy strategy) {
this.strategy = strategy;
}
public void formatVideo(Video video){
strategy.compress(video);
}
public static void main(String[] args)
{
//Video to be formatted/compressed
Video video = new Video();
VideoCompressor videoCompressor = new VideoCompressor();
videoCompressor.setCompressionStrategy(new MKV());
videoCompressor.formatVideo(video);
videoCompressor.setCompressionStrategy(new AVI());
videoCompressor.formatVideo(video);
videoCompressor.setCompressionStrategy(new MP4());
videoCompressor.formatVideo(video);
}
}
And this will be the output of the program:
For your reference, here are couple examples of singleton patterns used within Java EE APIs:
- java.utils.Collections - sort(List
list, Comparator c)
Thanks for reading and please don't hesitate to leave any comments.















0 comments: