Behavioral Design Patterns in Java

Overview

This is the third article in a short series dedicated to Design Patterns in Java, and a direct continuation from the previous article - Structural Design Patterns in Java.

Behavioral Patterns

Behavioral Patterns are concerned with providing solutions regarding object interaction - how they communicate, how are some dependent on others, and how to segregate them to be both dependent and independent and provide both flexibility and testing capabilities.

The Behavioral Patterns in Java that are covered in this article are:

Interpreter

The Interpreter pattern is used anytime we need to evaluate any kind of language grammar or expressions. A good example of this pattern would be Google Translate, which interprets the input, and shows us the output in another language.

Another example would be the Java compiler. The compiler interprets Java code and translates it into bytecode that the JVM uses to perform operations on the device it runs on.

This pattern also represents a great way to write simple programs that understand human-like syntax.

Implementation

We'll be making a simple implementation with simple grammar, otherwise, it would become convoluted and far too complex for the sake of this tutorial.

To realize this design pattern, we'll have to define an interpreter engine, accompanied by different expressions which it will use to interpret the command.

Let's define an interface for all of these expressions:

public interface Expression {
    public int interpret(InterpreterEngine engine);
}

This interpreter engine is simple:

public class InterpreterEngine {
    public int add(String input) {
        String[] tokens = interpret(input);
        int num1 = Integer.parseInt(tokens[0]);
        int num2 = Integer.parseInt(tokens[1]);
        return (num1+num2);
    }
    
    public int multiply(String input) {
        String[] tokens = interpret(input);
        int num1 = Integer.parseInt(tokens[0]);
        int num2 = Integer.parseInt(tokens[1]);
        return (num1*num2);
    }
     
    private String[] interpret(String input) {
        String string = input.replaceAll("[^0-9]", " ");
        string = string.replaceAll("( )+", " ").trim();
        String[] tokens = string.split(" ");
        return tokens;
    }
}

It replaces all non-digit characters with empty characters, and splits the input into tokens. This basically leaves us without digits.

Now, let's implement the Expression interface with a few concrete classes:

public class AddExpression implements Expression {
    private String expression;
    
    public AddExpression(String expression) {
        this.expression = expression;
    }
    
    @Override
    public int interpret(InterpreterEngine engine) {
        return engine.add(expression);
    }
}

public class MultiplyExpression implements Expression {
    private String expression;
    
    public MultiplyExpression(String expression) {
        this.expression = expression;
    }

    @Override
    public int interpret(InterpreterEngine engine) {
        return engine.multiply(expression);
    }
}

And to illustrate the point of the pattern:

public class Main {
    private InterpreterEngine engine;
    
    public Main(InterpreterEngine engine) {
        this.engine = engine;
    }
    
    public int interpret(String input) {
        Expression expression = null;
        
        if(input.contains("add")) {
            expression = new AddExpression(input);
        } else if(input.contains("multiply")) {
            expression = new MultiplyExpression(input);
        }
        
        int result = expression.interpret(engine);
        System.out.println(input);
        return result;
    }
    
    public static void main(String[] args) {
        Main main = new Main(new InterpreterEngine());
        
        System.out.println("Result: " + main .interpret("add 15 and 25"));
        System.out.println("Result: " + main .interpret("multiply " + main .interpret("add 5 and 5") + " and 10"));
    }
}

Since we discarded all non-digit characters, here's the place to evaluate whether the interpreter should add or multiply the input.

Running this piece of code will yield:

add 15 and 25
Result: 40
add 5 and 5
multiply 10 and 10
Result: 100

Template Method

The Template Method, otherwise known as Template Pattern is all around us. It boils down to defining an abstract class that provides predefined ways to run its methods. Sub-classes that inherit these methods must also follow the way defined in the abstract class.

In some cases, the abstract class may already include a method implementation, not just instructions, if it's a functionality that will be shared amongst all or most of the sub-classes.

Implementation

In a company, all employees have a few shared responsibilities:

public abstract class Employee {
    abstract void work();
    abstract void takePause();
    abstract void getPaid();
    
    public final void comeToWork() {
        work();
        takePause();
        work();
        getPaid();
    }
}

They all come to work, all get a break and get paid.

Different employees do different kinds of work:

public class Programmer extends Employee {

    @Override
    void work() {
        System.out.println("Writing code.");
    }

    @Override
    void takePause() {
        System.out.println("Taking a small break from writing code.");
    }

    @Override
    void getPaid() {
        System.out.println("Getting paid for developing the project.");
    }
}
public class Manager extends Employee {

    @Override
    void work() {
        System.out.println("Managing other employees.");
    }

    @Override
    void takePause() {
        System.out.println("Taking a small break from managing employees.");
    }

    @Override
    void getPaid() {
        System.out.println("Getting paid for overseeing the development of the project.");
    }
}

But they still follow the template of working, taking a pause, and getting paid, which is all laid out by the interface.

To illustrate the point of this pattern:

public class Main {
    public static void main(String[] args) {
        Employee employee = new Programmer();
        employee.comeToWork();
     
        System.out.println();
        
        employee = new Manager();
        employee.comeToWork();
    }
}

Running this piece of code will yield:

Writing code.
Taking a small break from writing code.
Writing code.
Getting paid for developing the project.

Managing other employees.
Taking a small break from managing employees.
Managing other employees.
Getting paid for overseeing the development of the project.

Chain of Responsibility

The Chain of Responsibility pattern is widely used and adopted. It defines a chain of objects, that collectively, one after another, process the request - where each processor in the chain has its own processing logic.

Each of these processing units decides who should continue processing the request next, and each has a reference to the next in line.

It's important to note that it's very handy for decoupling the sender from the receiver.

Implementation

As usual, let's define an abstract class:

public abstract class Employee {
    public static int PROGRAMER = 1;
    public static int LEAD_PROGRAMER = 2;
    public static int MANAGER = 3;
    
    protected int authorityLevel;
    
    protected Employee nextEmployee;
    
    public void setNextEmployee(Employee employee) {
        this.nextEmployee = employee;
    }
    
    public void doWork(int authorityLevel, String message) {
        if(this.authorityLevel <= authorityLevel) {
            write(message);
        }
        if(nextEmployee != null) {
            nextEmployee.doWork(authorityLevel, message);
        }
    }
    
    abstract protected void write(String message);
}

This abstract class contains authority levels for all of the employees. A programmer is located less on the hierarchy than a lead programmer, which is in turn, lower than a manager.

We've also included a reference to the next employee, which you'll see why is important soon enough.

A common method for all these classes is defined, with an authority check. If a certain class doesn't have the authority, it passes the request to the next one in the chain of responsibility.

Now, let's extend this class:

public class Programmer extends Employee {
    
    public Programmer(int authorityLevel) {
        this.authorityLevel = authorityLevel;
    }

    @Override
    protected void write(String message) {
        System.out.println("Programmer is working on project: " + message);
    }
}

public class LeadProgrammer extends Employee {
    
    public LeadProgrammer(int authorityLevel) {
        this.authorityLevel = authorityLevel;
    }

    @Override
    protected void write(String message) {
         System.out.println("Lead programmer is working on project: " + message);
    }
}

public class Manager extends Employee {
    
    public Manager(int authorityLevel) {
        this.authorityLevel = authorityLevel;
    }

    @Override
    protected void write(String message) {
         System.out.println("Manager is working on project: " + message);
    }
}

As mentioned above, each of these units provide their own processing logic.

To illustrate the point of this pattern:

public class Main {
    private static Employee getChainOfEmployees() {
        Employee programmer = new Programmer(Employee.PROGRAMER);
        Employee leadProgrammer = new LeadProgrammer(Employee.LEAD_PROGRAMER);
        Employee manager = new Manager(Employee.MANAGER);
        
        programmer.setNextEmployee(leadProgrammer);
        leadProgrammer.setNextEmployee(manager);

        return programmer;
    }

    public static void main(String[] args) {
        Employee employeeChain = getChainOfEmployees();
        
        employeeChain.doWork(Employee.PROGRAMER, "This is basic programming work.");
        employeeChain.doWork(Employee.LEAD_PROGRAMER, "This is marginally more 
            sophisticated programming work.");
        employeeChain.doWork(Employee.MANAGER, "This is the work for a manager.");
    }
}

First off, a static method getChainOfEmployees() is defined. This method is used to set the authority levels of each unit, via their constructors, and to define the order of responsibility.

By setting the next Employee for Programmer, we're basically telling it who to go to, if the request is out of scope for it.

Naturally, a programmer will turn to their designated LeadProgrammer. If the request is too much even for them to handle, they will turn to their Manager for help.

Running this piece of code will yield:

Programmer is working on project: This is basic programming work.
Programmer is working on project: This is marginally more sophisticated programming work.
Lead programmer is working on project: This is marginally more sophisticated programming work.
Programmer is working on project: This is the work for a manager.
Lead programmer is working on project: This is the work for a manager.
Manager is working on project: This is the work for a manager.

A Programmer is assigned to work on a request on their own authority level, and they do it gracefully.

Then, a new request comes in, needing the authority of a LeadProgrammer, so they take over.

Finally, another request comes in, needing the authority of a Manager. The programmer asks their designated lead programmer for help, which in turn decides to ask their manager for help, and the manager happily complies and does the job.

Command

Another decoupling design pattern, the Command pattern works by wrapping the request from the sender in an object called a command. This command is then passed to the invoker object, which proceeds to look for the adequate way to process the request.

Once it finds the adequate way, it passes the command, where it will be executed.

Implementation

Let's simulate a programmer's job for this pattern. A client can send an Order - a command, for an Application - a request. The programmer can then make the application, and sell it to the client.

Let's make our command:

public interface Order {
    void placeOrder();
}

And our request:

public class Application {
    private String name = "Computer Application";
    private int quantity = 2;
    
    public void make() {
        System.out.println(quantity + " application(s) are made for the client.");
    }
    
    public void sell() {
        System.out.println(quantity + "application(s) are sold to the client.");
    }
}

Assuming that the programmer accepted work with the client, it would be fitting to make the application:

public class MakeApplication implements Order {
    private Application application;
    
    public MakeApplication(Application application) {
        this.application = application;
    }
    
    @Override
    public void placeOrder() {
        application.make();
    }
}

And after making it, the programmer will proceed to sell it:

public class SellApplication implements Order {
    private Application application;
    
    public SellApplication(Application application) {
        this.application = application;
    }

    @Override
    public void placeOrder() {
        application.sell();
    }
}

An invoker object is needed, to which we send the request:

public class Programmer {
    private List<Order> orderList = new ArrayList<>();
    
    public void takeOrder(Order order) {
        orderList.add(order);
    }
    
    public void placeOrders() {
        for(Order order : orderList) {
            order.placeOrder();
        }
        orderList.clear();
    }
}

The request, even though it's an Application is wrapped as an Order - a command, as described before the implementation.

And to illustrate the point of this pattern:

public class Main {
    public static void main(String[] args) {
        // command
        Application application = new Application();
        
        / /wrapping requests
        MakeApplication makeApplication = new MakeApplication(application);
        SellApplication sellApplication = new SellApplication(application);

        // invoker
        Programmer programmer = new Programmer();
        programmer.takeOrder(makeApplication);
        programmer.takeOrder(sellApplication);

        // invoker processed the wrapped request
        programmer.placeOrders();
    }
}

Running this piece of code will yield:

2 application(s) are made for the client.
2 application(s) are sold to the client.

Iterator

The Iterator pattern is used as the core pattern of Java's Collection Framework. It's used to access the members of collections all the while hiding the underlying implementation.

Implementation

This is a fairly simple implementation and is used as the core pattern in multiple frameworks, including the framework mentioned above.

We will be making a simple iterator to print out the names of our employees.

All of our employees have their own sector in which they operate. So working under a sector also includes an iterator for all of them.

So let's go ahead and define our Iterator:

public interface Iterator {
    public boolean hasNext();
    public Object next();
}

This iterator will be stored in a container of sorts. In our case, a work Sector:

public interface Sector {
    public Iterator getIterator();
}

Now, let's define a repository for our employees:

public class EmployeeRepository implements Sector {
    public String[] employees = {"David", "Scott", "Rhett", "Andrew", "Jessica"};

    @Override
    public Iterator getIterator() {
        return new EmployeeIterator();
    }
    
    private class EmployeeIterator implements Iterator {
        int index;
        
        @Override
        public boolean hasNext() {
            if(index < employees.length) {
                return true;
            } 
            return false;
        }

        @Override
        public Object next() {
            if(this.hasNext()) {
                return employees[index++];
            }
            return null;
        }
    }
}

For the sake of simplicity, we've only used an array of Strings and avoided defining a seperate Employee class.

Free eBook: Git Essentials

Check out our hands-on, practical guide to learning Git, with best-practices, industry-accepted standards, and included cheat sheet. Stop Googling Git commands and actually learn it!

To illustrate the point of this pattern:

public class Main {
    public static void main(String[] args) {

        EmployeeRepository employeeRepository = new EmployeeRepository();

        for(Iterator iterator = employeeRepository.getIterator(); 
                iterator.hasNext();) {
            String employee = (String)iterator.next();
            System.out.println("Employee: " + employee);
        }
    }
}

Running this piece of code will yield:

Employee: David
Employee: Scott
Employee: Rhett
Employee: Andrew
Employee: Jessica

Mediator

Similar to the Adapter pattern, but with a different goal. The Mediator pattern acts as a bridge and, as the name implies, the mediator between different objects which communicate in any way. In large-scale applications, direct communication means tight-coupling which makes it hard to test, maintain and scale.

The Mediator pattern addresses this issue by acting as a third party over which the communication is done, decoupling them in the process.

Implementation

This is a fairly simple implementation, and probably the most notorious one is a chat between two individuals.

A User object wishes to communicate with another, so they use a mediator platform between them to do so - a Chat:

public class Chat {
    public static void showMessage(User user, String message) {
        System.out.println(new Date().toString() + "[" + user.getName() + "]: " + message);
    }
}

This class contains only one method and, accepting a User and a String, it formats the parameters and shows the message.

public class User {
    private String name;

    public User(String name) {
        this.name = name;
    }

    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }   

    public void sendMessage(String message) {
        Chat.showMessage(this, message);
    }
}

Our User class defines a sendMessage() method. This method calls upon the static method from the Chat class with this instance of the user and a String as the arguments.

To illustrate the point of this pattern:

public class Main {
    public static void main(String[] args) {
        User david = new User("David");
        User scott = new User("Scott");
        
        david.sendMessage("Hi Scott! How are you?");
        scott.sendMessage("I'm great! Thanks for asking. How are you?");
    }
}

These two objects don't communicate directly. None of them point to any reference variable or another object, yet the Chat class acts as a mediator and connects them.

Running this piece of code will yield:

Fri Aug 31 14:14:19 CEST 2018[David]: Hi Scott! How are you?
Fri Aug 31 14:14:19 CEST 2018[Scott]: I'm great! Thanks for asking. How are you?

Memento

The Memento pattern is concerned with previous states of the object. This means that the pattern is used when we want to save some state of an object, in the case we might want to restore the object to that state later on.

Implementation

This pattern relies on the work of three classes, also known as actor classes. The Memento object contains a state that we wish to save for later use. The Originator object creates and stores states in the Memento objects, while the CareTaker object takes care of the restoration process.

Let's firstly define our memento:

public class Memento {
    private String state;
    
    public Memento(String state) {
        this.state = state;
    }
    
    public String getState() {
        return state;
    }
}

Then our originator and caretaker:

public class Originator {
    private String state;
    
    public void setState(String state) {
        this.state = state;
    }
    
    public String getState() {
        return state;
    }
    
    public Memento saveStateToMemento() {
        return new Memento(state);
    }
    
    public void getStateFromMemento(Memento memento) {
        state = memento.getState();
    }
}
public class CareTaker {
    private List<Memento> mementoList = new ArrayList<>();
    
    public void add(Memento memento) {
        mementoList.add(memento);
    }
    public Memento get(int index) {
        return mementoList.get(index);
    }
}

And to illustrate the point of the pattern:

public class Main {
    public static void main(String[] args) {
        Originator originator = new Originator();
        CareTaker careTaker = new CareTaker();
        
        originator.setState("State 1 at " + System.currentTimeMillis());
        originator.setState("State 2 at " + System.currentTimeMillis());
        careTaker.add(originator.saveStateToMemento());
        
        originator.setState("State 3 at " + System.currentTimeMillis());
        careTaker.add(originator.saveStateToMemento());
        
        System.out.println("Current state: " + originator.getState());
        
        originator.getStateFromMemento(careTaker.get(0));
        System.out.println("First saved state: " + originator.getState());
        originator.getStateFromMemento(careTaker.get(1));
        System.out.println("Second saved state: " + originator.getState());
    }
}

Running this piece of code will yield:

Current state: State 3 at 1535705131218
First saved state: State 2 at 1535705131218
Second saved state: State 3 at 1535705131218

Observer

The Observer pattern is used to monitor the state of a certain object, often in a group or one-to-many relationship. In such cases, most of the time, the changed state of a single object can affect the state of the rest, so there must be a system to note the change and alert the other objects.

While Java does provide both a class and an interface with this pattern in mind, it's not widespread because it wasn't realized in an ideal way.

Implementation

To illustrate this pattern, we'll build a small office with a CEO, Manager, LeadProgrammer and a Programmer.

The programmer will be observed by his superiors, which have an opinion of him based on how well he does his job:

public class Programmer {
    private List<Observer> observers = new ArrayList<>();
    private String state;
    
    public String getState() {
        return state;
    }
    
    public void setState(String state) {
        this.state = state;
        notifyObservers();
    }
    
    public void attach(Observer observer) {
        observers.add(observer);
    }
    
    public void notifyObservers() {
        for (Observer observer : observers) {
            observer.update();
        }
    }
}

There's a one-to-many relationship with his observers, and each change of state notifies them all.

All of these observers have a couple of things in common:

public abstract class Observer {
    protected Programmer programmer;
    public abstract void update();
}

But each have their own implementation:

public class CEO extends Observer {

    public CEO(Programmer programmer) {
        this.programmer = programmer;
        this.programmer.attach(this);
    }
    
    @Override
    public void update() {
        if(this.programmer.getState().equalsIgnoreCase("Successful")) {
            System.out.println("CEO is happy with Manager and Lead Programmer.");
        } else {
            System.out.println("CEO is unhappy with Manager and Lead Programmer.");
        }
    }
}

public class Manager extends Observer {
    
    public Manager(Programmer programmer) {
        this.programmer = programmer;
        this.programmer.attach(this);
    }
    
    @Override
    public void update() {
        if(this.programmer.getState().equalsIgnoreCase("Successful")) {
            System.out.println("Manager is happy with Lead Programmer and this Programmer.");
        } else {
            System.out.println("Manager is unhappy with Lead Programmer and this Programmer.");
        }
    }
}

public class LeadProgrammer extends Observer {

    public LeadProgrammer(Programmer programmer) {
        this.programmer = programmer;
        this.programmer.attach(this);
    }
    
     @Override
    public void update() {
        if(this.programmer.getState().equalsIgnoreCase("Successful")) {
            System.out.println("Lead Programmer is proud of his Programmer.");
        } else {
            System.out.println("Lead Programmer is not proud of his Programmer.");
        }
    }
}

The CEO doesn't concern himself with the programmer, but rather the result, leaving it in the capable hands of the Manager and the LeadProgrammer. The manager is concerned mostly whether the lead programmer can guide the programmer to do his job. And finally, the lead programmer is mainly concerned by what the programmer does.

To illustrate the point of this pattern:

public class Main {
    public static void main(String[] args) {
        Programmer programmer = new Programmer();
        
        new CEO(programmer);
        new Manager(programmer);
        new LeadProgrammer(programmer);
        
        System.out.println("Programmer successfully did his job!");
        programmer.setState("Successful");
        System.out.println("Programmer failed his new assignment.");
        programmer.setState("Failed");
    }
}

Running this piece of code will yield:

Programmer successfully did his job!
CEO is happy with Manager and Lead Programmer.
Manager is happy with Lead Programmer and this Programmer.
Lead Programmer is proud of his Programmer.
Programmer failed his new assignment.
CEO is unhappy with Manager and Lead Programmer.
Manager is unhappy with Lead Programmer and this Programmer.
Lead Programmer is not proud of his Programmer.

State

The State pattern is used when a specific object needs to change its behavior, based on its state. This is accomplished by providing each of these objects with one or more state objects.

Based on these state objects, we can completely change the behavior of the concerned object.

Implementation

Let's define a simple interface:

public interface State {
    public void doAction(Context context);
}

This state will be carried through a context:

public class Context {
    private State state;
    
    public Context() {
        state = null;
    }
    
    public void setState(State state) {
        this.state = state;
    }
    
    public State getState() {
        return state;
    }
}

And two concrete classes implement it:

public class ApplicationStart implements State {

    @Override
    public void doAction(Context context) {
        System.out.println("The application is in the starting state of development.");
        context.setState(this);
    }
    public String toString() {
        return "Starting state.";
    }
}

public class ApplicationFinish implements State {

    @Override
    public void doAction(Context context) {
        System.out.println("The application is in the finished state of development.");
        context.setState(this);
    }
    public String toString() {
        return "Finished state.";
    }    
}

Now to illustrate the point of this pattern:

public class Main {
    public static void main(String[] args) {
        Context context = new Context();
        
        ApplicationStart start = new ApplicationStart();
        start.doAction(context);
        
        System.out.println(context.getState());
        
        ApplicationFinish finish = new ApplicationFinish();
        finish.doAction(context);
        
        System.out.println(context.getState());
    }
}

Running this piece of code will yield:

The application is in the starting state of development.
Starting state.
The application is in the finished state of development.
Finished state.

As you can see, the behavior of the carrier is changed by the state.

Strategy

The Strategy pattern is employed in situations where algorithms or class' behavior should be dynamic. This means that both the behavior and the algorithms can be changed at runtime, based on the input of the client.

Similar to the State Pattern, this pattern employs multiple strategy objects which define different strategies for the targeted class. The targeted class adapts its algorithms and behaviors based on these strategies.

Implementation

Let's start off with defining a strategy:

public interface Strategy {
    public String build(String location);
}

This strategy will be used to build different types of building, in different locations. These building types implement the strategy each in a different way:

public class Skyscraper implements Strategy {

    @Override
    public String build(String location) {
        return "Building a skyscraper in the " + location + " area.";
    }
}

public class House implements Strategy {

    @Override
    public String build(String location) {
        return "Building a house in the " + location + " area.";
    }
}

public class Mall implements Strategy {

    @Override
    public String build(String location) {
        return "Building a mall in the " + location + " area.";
    }
}

Similar to the State pattern, a Context class will use the strategy:

public class BuildContext {
    private Strategy strategy;
    
    public BuildContext(Strategy strategy) {
        this.strategy = strategy;
    }
    
    public String executeStrategy(String location) {
        return strategy.build(location);
    }
}

And to illustrate the point of this pattern:

public class Main {
    public static void main(String[] args) {
        BuildContext buildContext = new BuildContext(new Skyscraper());
        System.out.println("Requesting a skyscraper: " + buildContext.executeStrategy("Downtown"));
        
        buildContext = new BuildContext(new House());
        System.out.println("Requesting a house: " + buildContext.executeStrategy("Outskirts"));
        
        buildContext = new BuildContext(new Mall());
        System.out.println("Requesting a mall: " + buildContext.executeStrategy("City Centre"));
    }
}

Running this piece of code will yield:

Requesting a skyscrapper: Building a skyscrapper in the Downtown area.
Requesting a house: Building a house in the Outskirts area.
Requesting a mall: Building a mall in the City Centre area.

Visitor

The Visitor pattern is used to move the operational logic from each individual element of a group, into a new class, which does the operation for them utilizing the data from each individual element.

This is done by making all of the elements accept a "visitor". This visitor will perform changes in a separate class, without changing the structure of the visited class at all. This makes it easy to add new functionality without changing visited classes at all.

That being said, the objects don't have to be the same and can be unrelated, implementing different interfaces etc. An example would be an application that counts the number of users on a website. Some of these users are admins, some are customers, some are moderators etc.

Even though they can implement different interfaces and serve different functions, this pattern can help acquire the correct number of users.

Implementation

Each item in our shop will be able to accept a visitor:

public interface Item {
    public int accept(Visitor visitor);
}

And here's our visitor:

public interface Visitor {
    int visit(Pen pen);
    int visit(Notebook notebook);
}

Let's define concrete classes for our shop items:

public class Pen implements Item {
    private int price;
    private String model;
    
    public Pen(int price, String model) {
        this.price = price;
        this.model = model;
    }
    
    public int getPrice() {
        return price;
    }
    
    public String getModel() {
        return model;
    }

    @Override
    public int accept(Visitor visitor) {
        return visitor.visit(this);
    }
}
public class Notebook implements Item {
    private int price;
    private int numberOfPages;
    
    public Notebook(int price, int numberOfPages) {
        this.price = price;
        this.numberOfPages = numberOfPages;
    }
    
    public int getPrice() {
        return price;
    }
    
    public int getNumberOfPages() {
        return numberOfPages;
    }

    @Override
    public int accept(Visitor visitor) {
        return visitor.visit(this);
    }
}

And now let's implement the visitor interface and showcase this design pattern. The implementation class will have its own logic for calculating the price of the items, not the items themselves:

public class VisitorImpl implements Visitor {

    @Override
    public int visit(Pen pen) {
        int price = pen.getPrice();
        System.out.println(pen.getModel() + " costs " + price);
        return price;
    }

    @Override
    public int visit(Notebook notebook) {
        int price = 0;
        if(notebook.getNumberOfPages() > 250) {
            price = notebook.getPrice()-5;
        } else {
            price = notebook.getPrice();
        }
        System.out.println("Notebook costs " + price);
        
        return price;
    }
}

And to illustrate the point of the pattern:

public class StackAbuseJavaDesignPatterns {
    public static void main(String[] args) {
        Item[] items = new Item[]{new Pen(10, "Parker"), new Pen(5, "Pilot"), new Notebook(50, 150), new Notebook(75, 300)};
        
        int total = getTotalPrice(items);
        System.out.println("Total price of items: " + total);
    }
    
    private static int getTotalPrice(Item[] items) {
        Visitor visitor = new VisitorImpl();
        int result = 0;
        for(Item item : items) {
            result = result + item.accept(visitor);
        }
        return result;
    }
}

Running this piece of code will yield:

Parker costs 10
Pilot costs 5
Notebook costs 50
Notebook costs 70
Total price of items: 135

Conclusion

With this, all Behavioral Design Patterns in Java are fully covered, with working examples.

If you'd like to continue reading about Design Patterns in Java, the following article covers J2EE Design Patterns.

Last Updated: May 23rd, 2019
Was this article helpful?

Improve your dev skills!

Get tutorials, guides, and dev jobs in your inbox.

No spam ever. Unsubscribe at any time. Read our Privacy Policy.

David LandupAuthor

Entrepreneur, Software and Machine Learning Engineer, with a deep fascination towards the application of Computation and Deep Learning in Life Sciences (Bioinformatics, Drug Discovery, Genomics), Neuroscience (Computational Neuroscience), robotics and BCIs.

Great passion for accessible education and promotion of reason, science, humanism, and progress.

Make Clarity from Data - Quickly Learn Data Visualization with Python

Learn the landscape of Data Visualization tools in Python - work with Seaborn, Plotly, and Bokeh, and excel in Matplotlib!

From simple plot types to ridge plots, surface plots and spectrograms - understand your data and learn to draw conclusions from it.

© 2013-2024 Stack Abuse. All rights reserved.

AboutDisclosurePrivacyTerms