Contents

Design Patterns

Design patterns are typical solutions to commonly occurring problems in software design. They are like pre-made blueprints that can be customized to solve a recurring design problem in any code.

All patterns can be categorized by their intent, or purpose.

Creational Patterns

This pattern provides ways to create object creation in a manner that increases flexibility and reuse of code.

Factory Method

This pattern provides an interface for creating objects in a superclass, but allows subclasses to alter the type of objects that will be created.

Instead of new keyword, this pattern uses a method to instantiate the object.

Example: Generate buttons for cross platform apps.

Java

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
public class ShapeFactory {
	// variables, constructors, etc
	
	public Shape createShape(String type) {
		switch(type) {
		  case "CIRCLE":
		    return new Circle();
		  case "SQUARE":
		    return new Square();
		  default:
		    return null
		}
	}

}
1
2
3
4
5
6
public class Demo {
	public static void main(String args[]) {
		ShapeFactory shapeFactory = new ShapeFactory();
    Shape circle = shapeFactory.createShape("CIRCLE");
	}
}

Real world code examples:

DateTimeFormatterFactory.java

NumberFormat.java

Calendar.java

Builder

Create an object step by step using methods, rather than using a construtor. It provides a way of creating complex object in a step by step manner.

Example: adding parameters for a SQL statement

Java

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
public class Employee {
	private Integer id;
	private String name;
	private Title jobTitle;
  // other variables, constructors, etc
  
	public Employee setId(Integer id) {
		this.id = id;
		return this;
	}
	
	public Employee setName(String name) {
		this.name = name;
		return this;
	}
	
	public Employee setJobTitle(Title jobTitle) {
		this.jobTitle = jobTitle;
		return this;
	}
	
}
1
2
3
4
5
6
public class Demo {
	public static void main(String args[]) {
    Employee employee = new Employee();
    employee.setId(123).setName("John Doe").setJobTitle(Title.RECEPTIONIST)));
	}
}

Real world code examples:

StringBuilder.java

MapSqlParameterSource.java

Prototype

It simply clones an object. Used as a way to give inheritance without the complexity of having many classes inherit from each other.

Example: Create environment specific confg

Java

1
2
3
4
public interface Shape {
    public Shape clone();
    public void draw();
}
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
public class Circle implements Shape, Cloneable {
    public int radius;

    public Circle() {}

    // copy constructor
    public Circle(Circle target) {
        super(target);
        if (target != null) {
            this.radius = target.radius;
        }
    }

    @Override
    public Shape clone() {
        return new Circle(this);
    }
 
    @Override
    public void draw() {
        System.out.println("Drawing a circle.");
    }
}
1
2
3
4
5
6
7
public class Demo {
	public static void main(String args[]) {
    Circle circle = new Circle();
    circle.radius = 15;
    Circle anotherCircle = (Circle) circle.clone();
	}
}

A real world code example:

CLICommand.java

CLIAction.java

Singleton

It can only be instantiated once.

Examples: logger, database config, settings, etc

Java

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
// thread safe
public final class Singleton {
    private static volatile Singleton instance;
    public String value;

    private Singleton() {}

    public static Singleton getInstance() {
        Singleton result = instance;
        if (result != null) {
            return result;
        }
        synchronized(Singleton.class) {
            if (instance == null) {
                instance = new Singleton();
            }
            return instance;
        }
    }
}
1
2
3
4
5
public class Demo {
	public static void main(String args[]) {
    Singleton singleton = Singleton.getInstance();
	}
}

Real world code examples:

SQLErrorCodesFactory.java

Runtime.java

Structural Patterns

This pattern explains how to assemble objects and classes into larger structures, while keeping these structures flexible and efficient.

Facade

High level class/method/API to hide complexity in the code base. A facade class contains the low level systems as dependencies which then simplifies their operation.

Java

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
public class AudioFile {
    private String name;
    private String codecType;

    public AudioFile(String name) {
        this.name = name;
        this.codecType = name.substring(name.indexOf(".") + 1);
    }

    public String getCodecType() {
        return codecType;
    }

    public String getName() {
        return name;
    }
}
1
public interface Codec {}
1
2
3
public class MP3Codec implements Codec {
    public String type = "mp3";
}
1
2
3
public class FlacCodec implements Codec {
    public String type = "flac";
}
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
public class CodecFactory {
    public static Codec extract(AudioFile file) {
        String type = file.getCodecType();
        switch(type) {
				  case "mp3":
				    return new MP3Codec();
				  case "flac":
				    return new FlacCodec();
				  default:
				    return null
				}
    }
}
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
public class BitrateReader {
    public static AudioFile read(AudioFile file, Codec codec) {
        System.out.println("BitrateReader: reading file...");
        return file;
    }

    public static AudioFile convert(AudioFile buffer, Codec codec) {
        System.out.println("BitrateReader: writing file...");
        return buffer;
    }
}
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
public class AudioConversionFacade {
    public File convertVideo(String fileName, String format) {
        AudioFile file = new AudioFile(fileName);
        Codec sourceCodec = CodecFactory.extract(file);
        Codec destinationCodec;
        if (format.equals("mp3")) {
            destinationCodec = new MP3Codec();
        } else {
            destinationCodec = new FlacCodec();
        }
        AudioFile buffer = BitrateReader.read(file, sourceCodec);
        AudioFile result = BitrateReader.convert(buffer, destinationCodec);
        System.out.println("VideoConversionFacade: conversion completed.");
        return result;
    }
}

Real world code examples:

JdbcClient.java

DefaultJdbcClient.java

HttpClientFacade.java

Proxy

Basically to substitute an original object with another object.

Java

1
2
3
public interface CommandExecutor {
	public void runCommand(String cmd) throws Exception;
}
1
2
3
4
5
6
7
8
9
public class CommandExecutorImpl implements CommandExecutor {

	@Override
	public void runCommand(String cmd) throws IOException {
		Runtime.getRuntime().exec(cmd);
		System.out.println("'" + cmd + "' command executed.");
	}

}
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
public class CommandExecutorProxy implements CommandExecutor {

	private boolean isAdmin;
	private CommandExecutor executor;
	
	public CommandExecutorProxy(String user, String pwd) {
		if("Admin".equals(user) && "@dmin".equals(pwd)) isAdmin=true;
		executor = new CommandExecutorImpl();
	}
	
	@Override
	public void runCommand(String cmd) throws Exception {
		if(isAdmin) {
			executor.runCommand(cmd);
		} else if(cmd.trim().startsWith("rm")){
				throw new Exception("rm command is not allowed for non-admin users.");
		} else {
			executor.runCommand(cmd);
		}
	}

}
1
2
3
4
5
6
7
public class Demo {
	public static void main(String args[]) {
    CommandExecutor executor = new CommandExecutorProxy("user", "user!");
		executor.runCommand("ls -ltr");
		executor.runCommand("rm -rf abc.pdf");
	}
}

Real world examples:

SessionProxy.java

EventListenerProxy.java

Behavioral Patterns

This pattern takes care of effective communication and the assignment of responsibilities between objects.

Iterator

To iterate over collections in loops.

Java

1
2
3
public enum Title {
	SOFTWARE_ENGINEER, SENIOR_SOFTWARE_ENGINEER, MANAGER;
}
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
public class Employee {
	private Integer id;
	private String name;
	private Title jobTitle;
	
	public Employee(int id, String name, Title title) {
		this.id = id;
		this.name = name;
		this.title = title;
	}
  // other variables, constructors, getter, setter, etc
}
1
2
3
4
5
public interface EmployeeCollection {
	public void addEmployee(Employee employee);
	public void removeEmployee(Employee employee);
	public EmployeeIterator iterator(Title title);
}
1
2
3
4
public interface EmployeeIterator {
	public boolean hasNext();
	public Employee next();
}
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
public class EmployeeCollectionImpl implements EmployeeCollection {
	private List<Employee> employees;
	
	public EmployeeCollectionImpl() {
		employees = new ArrayList<>();
	}
	
	@Override
	public void addEmployee(Employee employee) {
		this.employees.add(employee);
	}
	
	@Override
	public void removeEmployee(Employee employee) {
		this.employees.remove(employee);
	}
	
	@Override
	public EmployeeIterator iterator(Title title) {
		return new EmployeeIteratorImpl(title, this.employees);
	}
	
	private class EmployeeIteratorImpl implements EmployeeIterator {
		private Title title;
		private List<Employee> employees;
		private int position;
		
		public EmployeeIteratorImpl(Title title, List<Employee> employeeList) {
			this.title = title;
			this.employees = employeeList;
		}
		
		@Override
		public boolean hasNext() {
			while (position < employees.size()) {
				Employee employee = employees.get(position);
				if (employee.getTitle().equals(this.title) 
					return true;
				else
					position++;
			}
			return false;
		}
		
		@Override
		public Employee next() {
			Employee employee = employees.get(position);
			position++;
			return employee;
		}
	}
}
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
public class Demo {
	public static void main(String args[]) {
    EmployeeCollection employees = new EmployeeCollectionImple();
    employees.addEmployee(new Employee(1, "John Doe", Title.SOFTWARE_ENGINEER));
    employees.addEmployee(new Employee(2, "Jane Doe", Title.MANAGER));
    
    EmployeeIterator iterator = employees.iterator(Title.MANAGER);
    while (iterator.hasNext()) {
	    Employee employee = iterator.next();
	    System.out.println(employee.toString());
    }
	}
}

Real world examples:

ArrayList.java

HashMap.java

Observer

Used by several objects to subscribe to changes to a particular object. (one to many)

Java

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
public class EventManager {
	Map<String, List<EventListener>> listeners new HashMap<>();
	
	public EventManager(String... eventTypes) {
		for (String eventType : eventTypes) {
			this.listeners.put(eventType, new ArrayList<>());
		}
	}
	
	public void subscribe(String eventType, EventListener listener) {
		List<EventListener> users = listeners.get(eventType);
		users.add(listener);
	}
	
	public void unsubscribe(String eventType, EventListener listener) {
		List<EventListener> users = listeners.get(eventType);
		users.remove(listener);
	}
	
	public void notify(String eventType, File file) {
		List<EventListener> users = listeners.get(eventType);
		for (EventListener listener : users) {
			listener.update(eventType, file);
		}
	}
}
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
public class FileEditor {
	public EventManager events;
	private File file;
	
	public FileEditor() {
		this.events = new EventManager("open", "save");
	}
	
	public void openFile(String filePath) {
		this.file = new File(filePath);
		event.notify("open", file);
	}
	
	public void saveFile() {
		if (this.file != null) {
			events.notify("save", file);
		}
	}
	
}
1
2
3
public interface EventListener {
    void update(String eventType, File file);
}
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
public class EmailNotificationListener implements EventListener {
    private String email;

    public EmailNotificationListener(String email) {
        this.email = email;
    }

    @Override
    public void update(String eventType, File file) {
        System.out.println("Email to " + email + ": Someone has performed " + eventType + " operation with the following file: " + file.getName());
    }
}
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
public class LogListener implements EventListener {
    private File log;

    public LogListener(String fileName) {
        this.log = new File(fileName);
    }

    @Override
    public void update(String eventType, File file) {
        System.out.println("Save to log " + log + ": Someone has performed " + eventType + " operation with the following file: " + file.getName());
    }
}
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
public class Demo {
	public static void main(String args[]) {
    Editor editor = new Editor();
    editor.events.subscribe("open", new LogOpenListener("/path/to/log/file.txt"));
    editor.events.subscribe("save", new EmailNotificationListener("admin@example.com"));

    editor.openFile("test.txt");
    editor.saveFile();
	}
}

Real world examples:

HttpSessionBindingListener.java

DestructionCallbackBindingListener.java

Mediator

A middle man between two objects/classes. (many to many)

Example: Air traffic controller that sits between the runways & airplains to provide coordination & communication.

Java

1
2
3
4
public interface Mediator {
	void addUser(User user);
	void sendMessage(User user, String message);
}
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
public class ChatMediator implements Mediator {
	private List<User> users;
	
	public ChatMediator() {
		this.users = new ArrayList<>();
	}
	
	@Override
	void addUser(User user) {
		this.users.add(user);
	}
	
	@Override
	void sendMessage(User sender, String message) {
		for (User user : this.users) {
			if (user != sender)
				user.receive(message);
		}
	}
	
}
1
2
3
4
5
public interface User {
  void setMediator(Mediator mediator);
  void send(String message);
  void receive(String message);
}
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
public class UserImpl implements User {
	private Mediator mediator;
	private String name;
	
	public UserImpl(String name){
		this.name = name;
	}
	
  @Override
  public void setMediator(Mediator mediator) {
      this.mediator = mediator;
  }
  
  @Override
  void send(String message) {
	  mediator.sendMessage(this, message);
  }
  
  @Override
  void receive(String message) {
	  System.out.println(this.name + " - Received message: " + message);
  }
  
}
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
public class Demo {
	public static void main(String args[]) {
    User user1 = new UserImpl(mediator, "John");
    User user2 = new UserImpl(mediator, "Jane");
    User user3 = new UserImpl(mediator, "Jake");
    
    Mediator mediator = new ChatMediator();
    mediator.addUser(user1);
    mediator.addUser(user2);
    mediator.addUser(user3);
    
    user1.send("Hello!");
	}
}

Real world examples:

ExecutorService.java

Method.java

State

Where an object behaves differently based on finite number of states.

Examples: Finite State Machine

Java

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
public class Package {
  private PackageState state = new OrderedState();

  public void previousState() {
    state.prev(this);
  }

  public void nextState() {
    state.next(this);
  }

  public void printStatus() {
    state.printStatus();
  }
  // other variables, constructors, getter, setter, etc
}
1
2
3
4
5
public interface PackageState {
  void prev(Package pkg);
  void next(Package pkg);
  void printStatus();
}
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
public class OrderedState implements PackageState {

  @Override
  public void prev(Package pkg) {
    System.out.println("The package is in its root state.");
  }
  
  @Override
  public void next(Package pkg) {
    pkg.setState(new ShippedState());
  }

  @Override
  public void printStatus() {
    System.out.println("Package ordered.");
  }
}
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
public class ShippedState implements PackageState {

  @Override
  public void prev(Package pkg) {
    pkg.setState(new OrderedState());
  }
  
  @Override
  public void next(Package pkg) {
    System.out.println(new DeliveredState());
  }
  
  @Override
  public void printStatus() {
    System.out.println("Package shipped.");
  }
}
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
public class DeliveredState implements PackageState {

  @Override
  public void prev(Package pkg) {
    pkg.setState(new ShippedState());
  }
  
  @Override
  public void next(Package pkg) {
   System.out.println("Package delivered to post office, not received yet.");
  }

  @Override
  public void printStatus() {
    System.out.println("Package delivered.");
  }
}
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
public class Demo {
	public static void main(String args[]) {
    Package pkg = new Package();
    pkg.printStatus();

    pkg.nextState();
    pkg.printStatus();

    pkg.nextState();
    pkg.printStatus();
	}
}

Real world examples:

LifecycleImpl.java

Phase.java

PartGenerator.java

PartGenerator.java

References