1. Introduction
2. Gang of Four (GoF) Patterns
3. J2EE Core Patterns
4. Singleton

1. Introduction

Design patterns provide a kind of template design solution for a typical problem. Think of them as guidelines to a clean, reusable design of your code. They illustrate structure and in general leave practical details to the implementor. E.g. State pattern does not impose nor mentions how the actual state changes should occur, there are different ways to implement this.

This is how the original inventors of the design pattern - the Gang of Four - describe it in their introduction:

Design patterns make it easier to reuse successful designs and architectures. Expressing proven techniques as design patterns makes them more accessible to developers of new systems. Design patterns help you choose design alternatives that make a system reusable and avoid alternatives that compromise reusability. Design patterns can even improve the documentation and maintenance of existing systems by furnishing an explicit specification of class and object interactions and their underlying intent. Put simply, design patterns help a designer get a design "right" faster.

It is not my intention to define the patterns or to reproduce information that can be found elsewhere. I will try, however, to clarify and comment on patterns from the perspective of my own experiences. And give concrete examples of some of them (which should be more and more over time).

The examples given are simplified, didactic pieces of code. I do my best to provide something meaningful that might resemble production code.

2. Gang of Four (GoF) Patterns

Creational patterns

Structural patterns

Behavioral patterns

3. J2EE Core Patterns

Presentation Layer

Business Layer

Data Layer

4. Singleton

4.1. Introduction

From the Book :

Ensure a class only has one instance, and provide a global point of access to it.

Probably the most overused design pattern. It is simple and easy to understand - and that is probably the cause of it.

In my opinion you probably never need this, in Java, it is a way to introduce application wide state - similar to the global variables in other languages and equally considered bad practice in those situations.

Avoid using singletons, rely on dependency injection instead.

J.B. Rainsberger wrote an interesting article about the problems with singletons.

Before we go to the examples, bear in mind that since a few years, using direct JDBC to access the database is probably not a good idea. Whenever possible use a JPA compliant approach such as Hibernate to implement persistence.

4.2. Singleton, Example to Avoid

Imagine an application that uses a database. You might consider accessing the database connection as a Singleton:

package be.ooxs.examples.designpatterns.singleton;

import be.ooxs.examples.designpatterns.abstractfactory.Order;

public class TheApplication {

	public void someBusinessMethod(Order order) {
		//apply some business logic here
		TheDatabase.getSingleton().save(order);
	}
}

You effectively freed the application from looking for a configuration, and managing the database connection.

Here is the TheDatabase class which is a Singleton. The things to look at to understand Singleton are: a private constructor, a static variable that contains the unique instance, the static method that lazily initializes - if null, then instantiate - the singleton.

package be.ooxs.examples.designpatterns.singleton;

import java.io.FileOutputStream;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.util.Properties;
import java.util.logging.Level;
import java.util.logging.Logger;

import be.ooxs.examples.designpatterns.abstractfactory.Order;

public class TheDatabase {
	private static TheDatabase singleton;
	private Connection connection;

	private TheDatabase() {

	}

	public static TheDatabase getSingleton() {
		if (singleton == null) {
			singleton = new TheDatabase();
		}
		return singleton;
	}

	public void open() {
		Properties configuration = new Properties();
		try {
			configuration.loadFromXML(getClass().getResourceAsStream("theDatabaseConfiguration.xml"));
			Class.forName(configuration.getProperty("driver.class"));
			String url = configuration.getProperty("jdbc.url");
			String user = configuration.getProperty("jdbc.user");
			String password = configuration.getProperty("jdbc.password");
			connection = DriverManager.getConnection(url, user, password);
		} catch (Exception exception) {
			Logger.getLogger("TheDatabase").log(Level.ALL, "open threw Exception", exception);
		}
	}

	public void close() {
		try {
			connection.close();
		} catch (Exception exception) {
			Logger.getLogger("TheDatabase").log(Level.ALL, "close threw Exception", exception);
		}
	}

	public void save(Order order) {
		try {
			PreparedStatement statement = connection.prepareStatement("UPDATE order SET productcode=?, description=? WHERE id=?;");
			statement.setString(1, order.getProductCode());
			// etc...

			statement.execute();
		} catch (SQLException exception) {
			Logger.getLogger("TheDatabase").log(Level.FINEST, "save threw SQLException", exception);
		}
	}
}

You would need a configuration file named theDatabaseConfiguration.xml in your classpath like this one:

<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!DOCTYPE properties SYSTEM "http://java.sun.com/dtd/properties.dtd">
<properties>
	<comment>configuration file for TheDatabase</comment>
	<entry key="jdbc.url">jdbc:hsqldb:file:../testdb</entry>
	<entry key="jdbc.user">theappuser</entry>
	<entry key="jdbc.password">supersecretpassword</entry>
	<entry key="driver.class">org.hsqldb.jdbcDriver</entry>
</properties>

There are a few problems with this approach.

  1. Unit testing becomes difficult, if you want to test TheApplication.someBusinessMethod(Order) you have to have some real database behind it.
  2. If you want to use two distinct databases (performance, gradual migration, legacy DB,...), you have an issue.
  3. ...

4.3. Dependency Injection Instead of Singleton

We could refactor this to remove the tight coupling and then add a secondary database we need to maintain for some legacy application, like this:

package be.ooxs.examples.designpatterns.singleton;

import be.ooxs.examples.designpatterns.abstractfactory.Order;

public class TheApplication2 {
	private TheDatabase2 database;
	private TheDatabase2 secondaryDatabase;

	public TheDatabase2 getDatabase() {
		return database;
	}

	public void setDatabase(TheDatabase2 database) {
		this.database = database;
	}

	public TheDatabase2 getSecondaryDatabase() {
		return secondaryDatabase;
	}

	public void setSecondaryDatabase(TheDatabase2 secondaryDatabase) {
		this.secondaryDatabase = secondaryDatabase;
	}

	public void someBusinessMethod(Order order) {
		//apply some business logic here
		database.save(order);

		//		if (legacyMaintained(order)) {
		//			extraDatabase.save(order);
		//		}
	}
}

Instead of looking for our configuration in here, we inverse control and use Spring IOC instead. We made the parameters accessible from the outside (STring paarmeters with getter and setter methods). We removed the Singleton code.

package be.ooxs.examples.designpatterns.singleton;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.util.Properties;
import java.util.logging.Level;
import java.util.logging.Logger;

import org.springframework.context.support.ClassPathXmlApplicationContext;

import be.ooxs.examples.designpatterns.abstractfactory.Order;

public class TheDatabase2 {
	private Connection connection;
	private String url;
	private String user;
	private String password;
	private String driverClass;

	// getters and setters for url, user, password and driverClass ...

	public void open() {
		Properties configuration = new Properties();
		try {
			configuration.loadFromXML(getClass().getResourceAsStream("theDatabaseConfiguration.xml"));
			Class.forName(driverClass);
			connection = DriverManager.getConnection(url, user, password);
		} catch (Exception exception) {
			Logger.getLogger("TheDatabase").log(Level.ALL, "open threw Exception", exception);
		}
	}

	public void close() {
		try {
			connection.close();
		} catch (Exception exception) {
			Logger.getLogger("TheDatabase").log(Level.ALL, "close threw Exception", exception);
		}
	}

	public void save(Order order) {
		try {
			PreparedStatement statement = connection.prepareStatement("UPDATE order SET productcode=?, description=? WHERE id=?;");
			statement.setString(1, order.getProductCode());
			// etc...

			statement.execute();
		} catch (SQLException exception) {
			Logger.getLogger("TheDatabase").log(Level.FINEST, "save threw SQLException", exception);
		}
	}
}

Somewhere we have to replace this

	TheApplication1 app = new TheApplication1();

with this:

	ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("be/ooxs/examples/designpatterns/singleton/thedatabase-config.xml");
	TheApplication2 instance = (TheApplication2) context.getBean("theapplication", TheApplication2.class);

And we need a slightly larger XML file (but take into account we defined a second TheDatabase2 instance).

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:util="http://www.springframework.org/schema/util" 
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd 
       http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-2.0.xsd"
       
       >
    
    <bean id="database" class="be.ooxs.examples.designpatterns.singleton.TheDatabase2">
    	<property name="url" value="jdbc:hsqldb:file:../testdb" />
    	<property name="user" value="theappuser" />
    	<property name="password" value="supersecretpassword" />
    	<property name="driverClass" value="org.hsqldb.jdbcDriver" />
    </bean>
    
    
    <bean id="extradatabase" class="be.ooxs.examples.designpatterns.singleton.TheDatabase2">
    	<property name="url" value="jdbc:mysql://server01/" />
    	<property name="user" value="theappuser" />
    	<property name="password" value="secretpassword" />
    	<property name="driverClass" value="com.mysql.jdbc.Driver" />
    </bean>
    
	<bean id="theapplication" class="be.ooxs.examples.designpatterns.singleton.TheApplication2">		
		<property name="database" ref="database" />		
		<property name="secondaryDatabase" ref="extradatabase" />							
	</bean>
</beans>