Java Fundamentals Tutorial: Design Patterns

19. Design Patterns

Design Patterns in Java

19.1. What are Design Patterns?

  • The core of solutions to common problems
  • All patterns have: name, the problem they solve (in context), solution, consequences
  • Types of patterns:

    • Creational - abstraction of instantiation process
    • Structural - composition of classes and objects in formation of larger structures
    • Behavioral - abstraction of algorithms and assignment of responsibility between objects

Why reinvent the wheel when you can follow a field-tested blueprint for solving your not-so-unique problem?

Many of the design patterns in use today and described here originate from the famous Gang of Four book: "Design Patterns: Elements of Reusable Object-Oriented Software" by Eric Gamma, Richard Helm, Ralph Johnson, and John Vlissides (Addison-Wesley Publishing Co., 1995; ISBN: 0201633612)

19.2. Singleton

  • Allow only one instance of a given class

    • The class holds a reference to its only instance
    • Lazy (on request) or static initialization (on load)
    • Private constructor to restrict external instantiation
  • Provide global point of access to the instance

    public static ClassName getInstance()
  • Support multiple instances in the future if the requirements change

    • Update getInstance to return multiple (possibly cached) instances if needed

public class MySingleton {
  private static final MySingleton instance = new MySingleton();

  public static MySingleton getInstance() {
    return instance;

  private MySingleton() {} // no client can do: new MySingleton()

  public void doX() {…}
  public void doY() {…}
  public String getA() {return …;}
  public int getB() {return …;}

public class Client {
  public void doSomething() {
    // doing something
    // doing something else
    int b = MySingleton.getInstance().getB();
    // do something with b

19.3. Factory Method

  • Define an interface for creating an object
  • Defer the actual creation to subclasses
  • Localizes the knowledge of concrete instances
  • Use when clients cannot anticipate the class of objects to create

Figure 10. Factory Method Pattern


public interface Logger {
  public void error(String msg);
  public void debug(String msg);
public class ConsoleLogger implements Logger {
  public void error(String msg) {System.err.println("ERROR: "+ msg);}
  public void debug(String msg) {System.err.println("DEBUG: "+ msg);}
public class FileLogger implements Logger {
  private PrintStream out;
  private FileLogger(String file) throws IOException {
    this.out = new PrintStream(new FileOutputStream(file), true);
  public void error(String msg) {out.println("ERROR: "+ msg);}
  public void debug(String msg) {out.println("DEBUG: "+ msg);}
public abstract class LoggerFactory {
  public abstract Logger getLogger();
  public static LoggerFactory getFactory(String f) throws Exception {
    return (LoggerFactory ) Class.forName(f).newInstance();
public class ConsoleLoggerFactory extends LoggerFactory {
  public Logger getLogger() { return new ConsoleLogger(); }
public class FileLoggerFactory extends LoggerFactory { //ignoring expt
  private Logger log = new FileLogger(System.getProperty("log.file"));
  public Logger getLogger() { return log; }

19.4. Abstract Factory

  • Interface for creating family of related objects
  • Clients are independent of how the objects are created, composed, and represented
  • System is configured with one of multiple families of products

Figure 11. Abstract Factory Pattern


public interface Restaurant {
    public Appetizer getAppetizer();
    public Entree getEntree();
    public Dessert getDessert();

public interface Appetizer { public void eat(); }
public interface Entree { public void eat(); }
public interface Dessert { public void enjoy(); }

public class AmericanRestaurant implements Restaurant {
    public Appetizer getAppetizer() { return new Oysters(); }
    public Entree getEntree() { return new Steak(); }
    public Dessert getDessert() { return new CheeseCake(); }
public class ItalianRestaurant implements Restaurant {
    public Appetizer getAppetizer() { return new Pizzette(); }
    public Entree getEntree() { return new Pasta(); }
    public Dessert getDessert() { return new Gelato(); }

public class Oysters implements Appetizer {
    public void eat() {
        System.out.println("Eating Rocky Mountain Oysters");

public class Gelato implements Dessert {
    public void enjoy() { System.out.println("Enjoying ice cream"); }

19.5. Adapter

  • Convert the interface of a class into another interface clients expect
  • Allow classes to work together despite their incompatible interfaces
  • Typically used in development to glue components together, especially if 3rd party libraries are used
  • Also known as wrapper

 * Adapts Enumeration interface to Iterator interface.
 * Does not support remove() operation.
public class EnumerationAsIterator implements Iterator {
  private final Enumeration enumeration;

  public EnumerationAsIterator(Enumeration enumeration) {
    this.enumeration = enumeration;

  public boolean hasNext() {
    return this.enumeration.hasMoreElements();

  public Object next() {
    return this.enumeration.nextElement();

   * Not supported.
   * @throws UnsupportedOperationException if invoked
  public void remove() {
    throw new UnsupportedOperationException("Cannot remove");

19.6. Composite

  • Compose objects into three structures to represent part-whole hierarchies
  • Treat individual objects and compositions of objects uniformly

Figure 12. Composite Design Pattern


public interface Executable { // Component
  public void execute();

public class XExecutable implements Executable { // Leaf X
  public void execute() { /* do X */ }

public class YExecutable implements Executable { // Leaf Y
  public void execute() { /* do Y */ }

public class ExecutableCollection implements Executable { // Composite
  protected Collection executables = new LinkedList();

  public void addExecutable(Executable executable) {

  public void removeExecutable(Executable executable) {

  public void execute() {
    for (Iterator i = this.executables.iterator(); i.hasNext();) {

19.7. Decorator

  • Attach additional responsibilities to an object dynamically and transparently (run-time)
  • An alternative to sub classing (compile-time)
  • Remove responsibilities no longer needed

Figure 13. Decorator Design Pattern


A typical example of the decorator pattern can be found in FilterOutputStream, FilterInputStream, FilterReader, and FilterWriter.

For example, observe how we can decorate an OutputStream:

OutputStream out = new FileOutputStream("somefile");
out = new BufferedOutputStream(out); // buffer before writing
out = CipherOutputStream(out,        // encrypt all data
out = new ZipOutputStream(out);      // compress all data
To write our own decorator, we could do:
public ByteCounterOutputStream extends FilterOutputStream {
  private int counter = 0;
  public ByteCounterOutputStream(OutputStream out) {super(out);}
  public void write(int b) throws IOException {
  public int getCounter() {return this.counter;}

We could then wrap out with a byte counter decorator:

ByteCounterOutputStream bout = new ByteCounterOutputStream(out);
for (int b =; b != -1; b = {
System.out.println("Written "+ bout.getCounter() + " bytes");

19.8. Chain of Responsibility

  • Decouple senders of requests from receivers
  • Allow more than one object to attempt to handle a request
  • Pass the request along a chain of receivers until it is handled
  • Only one object handles the request

    • Servlet Filter and FilterChain bend this rule
  • Some requests might not be handled

public abstract class MessageHandler {
  private MessageHandler next;
  public MessageHandler(MessageHandler next) { = next;
  public void handle(Message message) {
    if ( != null) {; }

public class SpamHandler extends MessageHandler {
  public SpamHandler(MessageHandler next) {
  public void handle(Message message) {
    if (isSpam(message)) {
      // handle spam
    } else {

If a client was given an instance of a handler (created at run time):

MessageHandler handler = new BlackListHandler(new SpamHandler(new ForwardingHandler(new DeliveryHandler(null))));

The client would simply do:


19.9. Observer / Publish-Subscribe

  • Define a one-to-many dependency between objects
  • When the observed object (subject) changes state, all dependents get notified and updated automatically
  • java.util.Observable, java.util.Observer

Figure 14. Observer Design Pattern


public class Employee extends Observable {
  public void setSalary(double amount) {
    this.salary = amount;
public class Manager extends Employee implements Observer {
  public void update(Observable o) {
    if (o instanceof Employee) {
      // note employee's salary, vacation days, etc.
public class Department implements Observer {
  public void public void update(Observable o) {
   if (o instanceof Employee) {
     // deduct employees salary from department budget
public class Accounting implements Observer {
  public void public void update(Observable o) {
   if (o instanceof Employee) {
     // verify that monthly expenses are in line with the forcast

19.10. Strategy

  • Defined family of interchangeable and encapsulated algorithms
  • Change algorithms independently of clients that use them
  • E.g: java.util.Comparator,

Figure 15. Strategy Design Pattern


public interface Comparator {
  public int compare(Object o1, Object o2);

public class DateComparator implements Comparator {
  public int compare(Object o1, Object o2) {
    return ((Date) o1).compareTo((Date) o2);

public class StringIntegerComparator implements Comparator {
  public int compare(Object o1, Object o2) {
    return Integer.parseInt((String) o1) -
           Integer.parseInt((String) o2);

public class ReverseComparator implements Comparator {
 private final Comparator c;
 public ReverseComparator(Comparator c) {this.c = c; }
 public int compare(Object o1, Object o2) {
   return, o1);

To sort integers represented as strings in descending order, you could do:

Arrays.sort(stringArray, new ReverseComparator(new StringIntegerComparator()));

The sort algorithm is independent of the comparison strategy.

19.11. Template

  • Define a skeleton of an algorithm
  • Defer some steps to subclasses
  • Redefine other steps in subclasses
  • Often a result of refactoring common code

Figure 16. Template Design Pattern


public interface Calculator {
  public void calculate(double operand);
  public double getResult();

public abstract CalculatorTemplate implements Calculator() {
  private double result;
  private boolean initialized = false;
  public final void calculate(double operand) {
    if (this.initialized) {
      this.result = this.doCalculate(this.result, operand);
    } else {
      this.result = operand;
      this.initialized = true;
  public final double getResult() {
    return this.result; // throw exception if !initialized
  protected abstract doCalculate(double o1, double o2);

public class AdditionCalculator extends CalculatorTemplate {
  protected doCalculate(double o1, double o1) {return o1 + o2; }

public class SubtractionCalculator extends CalculatorTemplate {
  protected doCalculate(double o1, double o1) {return o1 - o2; }

19.12. Data Access Object

  • Abstracts and encapsulates all access to a data source
  • Manages the connection to the data source to obtain and store data
  • Makes the code independent of the data sources and data vendors (e.g. plain-text, xml, LDAP, MySQL, Oracle, DB2)

Figure 17. DAO Design Pattern


public class Customer {
  private final String id;
  private String contactName;
  private String phone;
  public void setId(String id) { = id; }
  public String getId() { return; }
  public void setContactName(String cn) { this.contactName = cn;}
  public String getContactName() { return this.contactName; }
  public void setPhone(String phone) { = phone; }
  public String getPhone() { return; }

public interface CustomerDAO {
  public void addCustomer(Customer c) throws DataAccessException;
  public Customer getCustomer(String id) throws DataAccessException;
  public List getCustomers() throws DataAccessException;
  public void removeCustomer(String id) throws DataAccessException;
  public void modifyCustomer(Customer c) throws DataAccessException;

public class MySqlCustomerDAO implements CustomerDAO {
  public void addCustomer(Customer c) throws DataAccessException {
    Connection con = getConnection();
    PreparedStatement s = con.createPreparedStatement("INSERT ...");