Behavioral Design Patterns

Behavioral Design Patterns

2020-03-16T13:46:37.312Z

Sources:

Memento

Restore prior state

The intent is to undo an operation.

To restore an object to a prior state, create classes to create, encapsulate and store copies of state:

  • the originator, which owns the state and makes a copy of the state,
  • the memento, which is the copy of the originator's state, and
  • the caretaker, which stores a list of copies of the originator's state.

The originator creates a copy of its state, encapsulates it in a memento (state snapshot) and stores it in the caretaker (collection of state snapshots). To restore its prior state, the originator sets its current state to the caretaker's memento, thus rolling back its own state and undoing the latest operation.

Editor.java
// Originator (owner of state and creator of state copy)
public class Editor {
	private String content;

	// make copy of state
	public EditorState createState() {
		return new EditorState(content);
	}

	// set current state based on previous state
	public void restore(EditorState state) {
		content = state.getContent();
	}

	public String getContent() {
		return content;
	}

	public void setContent(String content) {
		this.content = content;
	}
}
EditorState.java
// Memento (snapshot of state)
public class EditorState {
	private final String state;

	public EditorState(String content) {
		this.state = content;
	}

	public String getState() {
		return state;
	}
}
History.java
// Caretaker (collection of state snapshots)
public class History {
	private List<EditorState> states = new ArrayList<>();

	// add state snapshot to collection at the end
	public void push(EditorState state) {
		states.add(state);
	}

	// get latest state snapshot
	public EditorState pop() {
		var lastIndex = states.size() - 1;
		var lastState = states.get(lastIndex);
		states.remove(lastState);

		return lastState;
	}
}

State

Delegate functionality to a state object.

The intent is to have functionality be determined by state.

To use state-specific methods, create classes for all possible states and place all state-specific methods into those classes.

The object context stores a reference to a state object representing the context's current state. The functionality of all methods is delegated to the current state object through an interface of common methods.

Canvas.java
// Context
public class Canvas {
	private Tool currentTool;

	public void mouseDown() {
		currentTool.mouseDown();
	}

	public void mouseUp() {
		currentTool.mouseUp();
	}

	public Tool getCurrentTool() {
		return currentTool;
	}

	public void setCurrentTool(Tool currentTool) {
		this.currentTool = currentTool;
	}
}
Tool.java
public interface Tool {
	void mouseDown();
	void mouseUp();
}
BrushTool.java
// State
public class BrushTool implements Tool {
	@Override
	public void mouseDown() {
		System.out.println("Brush selected!");
	}

	@Override
	public void mouseUp() {
		System.out.println("Drawing a line...");
	}
}
EraserTool.java
// State
public class EraserTool implements Tool {
	@Override
	public void mouseDown() {
		System.out.println("Eraser selected!");
	}

	@Override
	public void mouseUp() {
		System.out.println("Erasing...");
	}
}

Iterator

Decouple traversal from a collection.

Extract the traversal behavior into an iterator object implementing an iterator interface with the current(), next() and hasNext() methods.

BrowseHistory.java
// Collection
public class BrowseHistory {
	private String[] urls = new String[10];
	private int count;

	public void push(String url) {
		urls[count++] = url;
	}

	public String pop() {
		return urls[--count];
	}

	public Iterator createIterator() {
		return new ArrayIterator(this);
	}

	// nested class implementing Iterator interface
	public class ArrayIterator implements Iterator {
		private BrowseHistory history;
		private int index;

		public ArrayIterator(BrowseHistory history) {
			this.history = history;
		}

		@Override
		public boolean hasNext() {
			return (index < history.count);
		}

		@Override
		public String current() {
			return history.urls[index];
		}

		@Override
		public void next() {
			index++;
		}
	}
}
Iterator.java
public interface Iterator {
	String current();
	void next();
	boolean hasNext();
}

Strategy

Decouple method variants from a class.

In a class called context having a method with many variants, extract the method variants into separate classes called strategies implementing an interface of common methods. The context delegates the operation to the selected strategy, whether stored in a field in the context or passed in as an argument.

Note: Strategy and Template Method both separate method variants from the class using them, but Strategy and Template Method differ in that...

  • Strategy uses interfaced classes (polymorphism without inheritance), whereas
  • Template Method uses subclasses (polymorphism with inheritance) implementing abstract methods.
ImageStorage.java
// Context
public class ImageStorage {
	public void store(String fileName, Compressor compressor, Filter filter) {
		compressor.compress(fileName); // delegate operation to passed-in compressor
	}
}

Compressor.java
public interface Compressor {
  void compress(String fileName);
}
JpegCompressor.java
// Strategy
public class JpegCompressor implements Compressor {
	@Override
	public void compress(String fileName) {
		System.out.println("Compressing using JPEG");
	}
}
PngCompressor.java
// Strategy
public class PngCompressor implements Compressor {
	@Override
	public void compress(String fileName) {
		System.out.println("Compressing using PNG");
	}
}

Template Method

Decouple method variants from a class.

Create an abstract class having abstract methods (stubs to be implemented by other classes) as well as ordinary methods. Create multiple concrete classes that implement the abstract methods in the abstract class. Have the abstract class delegate operations in abstract methods to the concrete classes.

Instead of creating abstract methods, you can also make them concrete but overridable methods with a default implementation, called hooks.

Note: Strategy and Template Method both separate method variants from the class using them, but Strategy and Template Method differ in that...

  • Strategy uses interfaced classes (polymorphism without inheritance), whereas
  • Template Method uses subclasses (polymorphism with inheritance) implementing abstract methods.
AuditTrail.java
// Logger (incidental to example)
public class Auditor {
	public void record() {
		System.out.println("Auditing...");
	}
}
Task.java
public abstract class Task {
	// `abstract`, so not to be used directly!
	private Auditor auditor;

	// constructor without parameter
	public Task() {
		auditor = new Auditor();
	}

	// constructor with parameter
	public Task(Auditor auditor) {
		this.auditor = auditor;
	}

	// delegate execution to subclass, which is to be used directly
	public void execute() {
		concretelyExecute();
	}

	protected abstract void concretelyExecute();
	// `protected` means that `doExecute` is invisible
	// from `Main.java` but visible in children!
}
TransferMoneyTask.java
// Subclass (without recording)
public class TransferMoneyTask extends Task {
  @Override
	protected void concretelyExecute() {
		System.out.println("Transferring money...");
	}
}
GenerateReportTask.java
// Subclass (with recording)
public class GenerateReportTask {
	private Auditor auditor;

	public GenerateReportTask(Auditor auditor) {
		this.auditor = auditor;
	}

	public void concretelyExecute() {
		auditor.record();
		System.out.println("Generating report...");
	}
}

Command

Decouple generic functionality from a class.

Create a class called invoker that sends a command to execute functionality. Create a class ConcreteCommand that encapsulates all the information for executing that command, implementing the Command interface with an execute() method. Have ConcreteCommand use that method to delegate work to a class called receiver, which encapsulates the business logic to execute the requested functionality. The invoker is thus decoupled from the receiver, i.e. the invoker is indirectly commanding the receiver.

The classic example is extracting the save functionality in a text editor, so that multiple classes may use that functionality, such as a save button, or an item in the context menu, or a keyboard shortcut.

Command.java
public interface Command {
  void execute();
}
Button.java
// Invoker
public class Button {
	private String label;
	private Command command;

	public Button(Command command) {
		this.command = command;
	}

	public void click() {
		command.execute(); // sends command
	}
}
AddCustomerCommand.java
// Concrete command
public class AddCustomerCommand implements Command {
	private CustomerService service;

	public AddCustomerCommand(CustomerService service) {
		this.service = service;
	}

	@Override
	public void execute() {
		service.addCustomer();
	}
}
CustomerService.java
// Receiver
public class CustomerService {
	public void addCustomer() {
		System.out.println("Add customer");
	}
}
Main.java
public class Main {
	public static void main(String[] args) {
		var service = new CustomerService(); // receiver
		var command = new AddCustomerCommand(service); // concrete command
		var button = new Button(command); // invoker
		button.click(); // invoker → concrete command → receiver
	}
}

The above AddCustomerCommand holds a single command, but you can also create a composite command implementing the Command interface.

CompositeCommand.java
public class CompositeCommand implements Command {
	private List<Command> commands = new ArrayList<>();

	public void add(Command command) {
		commands.add(command);
	}

	@Override
	public void execute() {
		for (var command : commands)
		command.execute();
	}
}
Main.java
public class Main {
	public static void main(String[] args) {
		var compositeCommand = new CompositeCommand();
		composite.add(new ResizeCommand());
		composite.add(new ApplyFilterCommand());
		composite.execute(); // execute two commands sequentially
	}
}

Observer

Have an object broadcast state changes.

Create an object called subject (or observable or publisher) that holds state. Create multiple objects called listeners (or observers or subscribers) interested in the publisher's state. Have the subject notify the subscribers, through a stream of events, of every change in the subject's state. When notified, each subscriber acts in response to the subject's new state.

Subject.java
// Setup
public class Subject {
	private List<Observer> observers = new ArrayList<>(); // storage for observers

	public void addObserver(Observer observer) {
		observers.add(observer);
	}

	public void removeObserver(Observer observer) {
		observers.remove(observer);
	}

	public void notifyObservers() {
		for (var observer : observers)
			observer.update();
	}
}
Observer.java
// Setup
public interface Observer {
  void update();
}
ValueSubject.java
// Subject (holds state)
public class ValueSubject extends Subject {
	private int value;

	public int getValue() {
		return value;
	}

	public void setValue(int value) {
		this.value = value;
		notifyObservers(); // communicating state change, without sending value
	}
}
ConcreteObserver.java
// Observer (reacts to state)
public class ConcreteObserver implements Observer {
	private ValueSubject valueSubject; // stores reference to subject

	public ConcreteObserver(ValueSubject valueSubject) {
		this.valueSubject = valueSubject;
	}

	@Override
	public void update() { // called from Subject method
		System.out.println("ValueSubject was notified: " + valueSubject.getValue());
	}
}

Besides communicating that state has changed, you can also send a value to the observer using:

  • the push style of communication, and
  • the pull style of communication.

Push style of communication

The subject sends the value to the observer.

Subject.java
// Setup
public class Subject {
	private List<Observer> observers = new ArrayList<>(); // storage for observers

	public void addObserver(Observer observer) {
		observers.add(observer);
	}

	public void removeObserver(Observer observer) {
		observers.remove(observer);
	}

	public void notifyObservers(int value) {
		for (var observer : observers)
			observer.update(value);
	}
}
Observer.java
// Setup
public interface Observer {
  void update();
}
ValueSubject.java
// Subject (holds state)
public class ValueSubject extends Subject {
	private int value;

	public int getValue() {
		return value;
	}

	public void setValue(int value) {
		this.value = value;
		notifyObservers(value);
		// communicating state change and pushing out (sending) value
	}
}
ConcreteObserver.java
// Observer (reacts to state)
public class ConcreteObserver implements Observer {
	private ValueSubject valueSubject;

	public ConcreteObserver(ValueSubject valueSubject) {
		this.valueSubject = valueSubject;
	}

	@Override
	public void update(int value) { // receiving value sent by `ValueSubject`
		System.out.println("ValueSubject was notified: " + value);
	}
}

Pull style of communication

The observer gets the value from the subject.

Subject.java
// Setup
public class Subject {
	private List<Observer> observers = new ArrayList<>(); // storage for observers

	public void addObserver(Observer observer) {
		observers.add(observer);
	}

	public void removeObserver(Observer observer) {
		observers.remove(observer);
	}

	public void notifyObservers() {
		for (var observer : observers)
			observer.update(); // no longer sending value
	}
}
ConcreteObserver.java
// Observer (reacts to state)
public class ConcreteObserver implements Observer {
	private ValueSubject valueSubject;

	public ConcreteObserver(ValueSubject valueSubject) {
		this.valueSubject = valueSubject;
	}

	@Override
	public void update(int value) {
		System.out.println("ValueSubject was notified: " +
		  valueSubject.getValue()); // pulling in value
	}
}

Mediator

Have an object centrally coordinate functionality.

Create an object called mediator that encapsulates business logic. Create objects called components that notify the mediator whenever their component state is changed.

The Mediator centrally coordinates interrelated objects, e.g. a text form object coordinates many fields, checkboxes and buttons, each with their own state and partially dependent on each other.

DialogBox.java
// Mediator parent
// Whenever it changes state, the UI component will call `changed`.
public abstract class DialogBox {
  public abstract void changed(UIControl uiControl);
}
UIControl.java
// Component parent
public class UIControl {
	protected DialogBox owner; // hold reference to mediator, visible to subclass

	public UIControl(DialogBox owner) {
		this.owner = owner;
	}
}
ListBox.java
// Component child
public class ListBox extends UIControl {
	private String selection;

	public ListBox(DialogBox owner) {
		super(owner);
	}

	public String getSelection() {
		return selection;
	}

	public void setSelection(String selection) {
		this.selection = selection;
		owner.changed(this); // notify DialogBox that ListBox's state has changed!
	}
ArticlesDialogBox.java
// Mediator child
public class ArticlesDialogBox extends DialogBox {
	// store contained components in fields
	private ListBox articlesListBox = new ListBox(this);

	// imagine implementation, similar to `articlesListBox`
	private TextBox titleTextBox = new TextBox(this);

	// imagine implementation, similar to `articlesListBox`
	private Button saveButton = new Button(this);

	@Override
	// react to change notification coming from method of component
	// e.g. `setSelection` in ListBox
	public void changed(UIcontrol uiControl) {
	if (control == articlesListBox)
		articleSelected();
	else if (control == titleTextBox)
		titleChanged();
	}

	private void titleChanged() {
		var content = titleTextBox.getContent();
		var isEmpty = (content == null || content.isEmpty());
		saveButton.setEnabled(!isEmpty);
	}

	private void articleSelected() {
		titleTextBox.setContent(articlesListBox.getSelection())
		saveButton.setEnabled(true)
	}
}

Implementing Mediator using Observer

EventHandler.java
// Observer
public interface EventHandler {
  void handle();
}
UIControl.java
// Component parent storing event handlers
public abstract class UIControl {
	private List<EventHandler> eventHandlers = new ArrayList<>();

	public void addEventHandler(EventHandler observer) {
		eventHandlers.add(observer);
	}

	protected void notifyEventHandlers() {
		for (var observer : eventHandlers)
		observer.handle();
	}
}
Button.java
// Component child
public class Button extends UIControl {
	private boolean isEnabled;

	public boolean isEnabled() {
		return isEnabled;
	}

	public void setEnabled(boolean enabled) {
		isEnabled = enabled;
		notifyEventHandlers(); // notify observer
	}
}
ArticlesDialogBox.java
// Mediator (unlike above, no longer a child!)
public class ArticlesDialogBox {
	// store contained components in fields
	private ListBox articlesListBox = new ListBox();
	private TextBox titleTextBox = new TextBox();
	private Button saveButton = new Button();

	public void ArticlesDialogBox() {
		// with references to observer method
		articlesListBox.addEventHandler(this::articleSelected);
		titleTextBox.addEventHandler(this::titleChanged);
	}

	private void titleChanged() {
		var content = titleTextBox.getContent();
		var isEmpty = (content == null || content.isEmpty());
		saveButton.setEnabled(!isEmpty);
	}

	private void articleSelected() {
		titleTextBox.setContent(articlesListBox.getSelection())
		saveButton.setEnabled(true)
	}
}

Chain of Responsibility

Link operations into a procedure.

Create one class called handler for each step in a procedure, link these handlers into a chain, and have each handler process the request and pass it along to the next handler. A handler may also not process the request, terminating all processing.

For example, think of an HTTP request that needs to be authenticated, compressed and logged:

Handler.java
public abstract class Handler {
	private Handler next; // reference to next handler

	public Handler(Handler next) { // creation requires setting next handler
		this.next = next;
	}

	// actual logic delegated to implementing class
	public abstract boolean delegatedHandle(HttpRequest request);

	public void handle(HttpRequest request) {
		if (delegatedHandle(request)) // if `true`, terminate processing
			return;

		if (next != null)
			next.handle(request);
		}
}
Authenticator.java
// First handler as subclass of Handler
public class Authenticator extends Handler {
	public Authenticator(Handler next) {
		super(next); // call parent constructor with passed in `next` handler
	}

	@Override
	public boolean delegatedHandle(HttpRequest request) {
		var isValid = (request.getUsername() == "admin" &&
		  request.getPassword() == "1234");

		// if invalid request, terminate all processing
		// (`true` as a response to "Should I stop?")
		if (!isValid) return true;

		System.out.println("Authenticating...");

		// valid request, continue processing
		// (`false` as a response to "Should I stop?")
		return false;
	}
}
Compressor.java
// Second handler as subclass of Handler
public class Compressor extends Handler {
	public Compressor(Handler next) {
		super(next);
	}

	@Override
	public boolean delegatedHandle(HttpRequest request) {
		System.out.println("Compressing...");
		return false;
	}
}
Logger.java
// Third and final handler as subclass of Handler
public class Logger extends Handler {
	public Logger(Handler next) {
		super(next);
	}

	@Override
	public boolean delegatedHandle(HttpRequest request) {
		System.out.println("Compressing...");
		return false;
	}
}
WebServer.java
// Object that receives request and kicks off handling
public class WebServer {
	private Handler handler;

	public WebServer(Handler handler) {
		this.handler = handler;
	}

	public void handle(HttpRequest request) {
		handler.handle(request);
	}
}
Main.java
public class Main {
	public static void main(String[] args) {
		// processing chain: authenticator → logger → compressor

		// third and final handler, so `next` is set to `null`
		var compressor = new Compressor(null);

		// second handler pointing to third handler
		var logger = new Logger(compressor);

		// first handler pointing to second
		var authenticator = new Authenticator(logger);

		// first handler as kickoff of `server`
		var server = new WebServer(authenticator);

		// kickoff
		server.handle(new HttpRequest("admin", "1234"));
	}
}

Visitor

Decouple an operation on an object from the object.

The intent of this separation is to run a standalone operation on an object without adding that operation as a methods inside the object.

  1. Create a class called a visitor representing a standalone operation. The visitor class should implement an interface with overloaded methods (i.e. same name, different signature).

  2. Create a class representing the object that will be "visited" by the operation. The visited object should have an execute() method that accepts a visitor operation and calls it on one of the overloaded methods in the visitor.

  3. Create a class to hold the to-be-visited objects and to run on all of them a visitor operation.

Operation.java
// Interface with overloaded methods
public interface Operation {
	void apply(HeadingNode heading);
	void apply(AnchorNode anchor);
}
HighlightOperation.java
// Class implementing visitor interface
public class HighlightOperation implements Operation {
	@Override
	public void apply(HeadingNode heading) {
		System.out.println("Highlighting heading...");
	}

	@Override
	public void apply(AnchorNode anchor) {
		System.out.println("Highlighting anchor...");
	}
}
HtmlNode.java
// Interface with single operation-accepting method
public interface HtmlNode {
	  void execute(Operation operation);
}
HeadingNode.java
// Class implementing the visited-object interface
public class HeadingNode implements HtmlNode {
  @Override
  public void execute(Operation operation) {
    operation.apply(this); // `apply` in its HeadingNode argument variant
  }
}
AnchorNode.java
// Class implementing "visited object" interface
public class AnchorNode implements HtmlNode {
  @Override
  public void execute(Operation operation) {
    operation.apply(this); // `apply` in its AnchorNode argument variant
  }
}
HtmlDocument.java
// Class for executing operations
public class HtmlDocument {
	private List<HtmlNode> nodes = new ArrayList<>();

	public void add(HtmlNode node) {
		nodes.add(node);
	}

	public void runOnAllNodes(Operation operation) {
		for (var node : nodes)
			node.execute(operation);
	}
}

In other words...

When you call runOnAllNodes() with an operation, each node to be visited calls its execute() method on that operation, passed in as an argument.

node.execute(operation);

This body of the node's execute() method inverts the caller and the argument: the node (originally, the caller) and the operation (originally, the argument) switch roles, so that the node becomes the argument and the operation becomes the caller.

public void execute(Operation operation) {
	operation.apply(this); // `this` points to `AnchorNode` or `HeadingNode`
}

You call an object's method on an operation, only for that operation to become the caller of one of its methods on that object, that is, you pass in an operation to an object to visited, only for the operation to "call itself" on that object. The appropriate method is selected according to the specific class of the object that made the initial call.

This allows you to create and run any operation on the object without modifying it internally. You just create a class for a new operation implementing the Operation interface, and then call runOnAllNodes() on an instance of that newly created operation.