avatarPeterson C

Summary

The website content provides an explanation of the Chain of Responsibility design pattern, detailing its structure, usage, and implementation with a practical example in the context of a surgical request approval system.

Abstract

The Chain of Responsibility pattern is a behavioral design pattern that allows handling requests without specifying the receiver explicitly. It passes requests along a chain of handlers, where each handler decides whether to process the request or pass it to the next handler. This pattern is ideal for scenarios where multiple objects could potentially handle a request, and the handler is not known in advance. The content illustrates this pattern through a surgical request system where surgeons of different specialties form a chain to approve or reject surgical requests based on their area of expertise. The implementation includes defining specialties, creating a surgical request class, and establishing an abstract surgeon class with concrete surgeon subclasses. The chain is constructed by linking these surgeons, and the pattern's functionality is demonstrated with a main method that simulates the handling of surgical requests.

Opinions

  • The author emphasizes the pattern's utility in systems where there is a clear order of escalation or processing.
  • The real-world example of a surgical request system is used to make the concept more relatable and understandable.
  • The pattern is presented as a flexible way to handle requests, with the potential to simplify the code by decoupling the sender of a request from its receiver.
  • The author suggests that the Chain of Responsibility pattern can improve the modularity of a system by allowing handlers to be added or removed without affecting the overall structure.
  • The code examples provided are simplified for clarity, with the assumption that input validation is handled separately.

A guide to software design patterns

Behavioral Design Pattern: Chain of Responsibility

Photo by Miltiadis Fragkidis on Unsplash

The chain of responsibility pattern is a design pattern consisting of a source of command objects and a series of processing objects.

This simply means that each object in the chain behaves as an object-oriented version of an if-condition. When a request comes in the chain, if it cannot be handled by the first object, it gets passed on to the next object and so forth until it’s handled or rejected if no object can handle it.

Each object contains specific logic on what level or type of request they can handle. Each object must have a way to add the next processing object to the chain. When an object has no next object, that is an indicator that you’ve reached the end of the chain.

Pattern Ideal Usage

This pattern should be used when there is a clear order of escalation or processing. I always find it easier to grasp concepts with a real-world scenario. Let’s use one and see how we would implement it using this pattern.

Let’s say you’re tasked to design a system that approves or rejects incoming surgical request procedures for a hospital. Each one of these requests will have a patient’s information and the needed specialty. If the specialty in the request is supported, then the surgery is approved. If the specialty in the request isn’t supported and there is a next object in the chain, then the request gets passed to the next object, otherwise, it gets rejected. Let’s look at how we would implement this in code.

Implementation

We won’t be doing any validation and assume all values are valid. It’s always up to you to make sure all user input is validated. With that being said, let’s get started.

We need to start by defining the specialties. We’ll create an enum called Specialties. Here’s the code for our enum.

Here’s the code for the enum:

public enum Specialties {
    Heart,
    Lungs,
    Oesophagus,
    Chest,
    Veins,
    Arteries,
    Head,
    Neck,
    Voice,
    Ear,
    Nose,
    Kidney,
    Bladder
}

Next, we need a request. The request will have a patient’s first and last name and the needed specialty and named SurgicalRequest.

Here’s the code for the request:

public class SurgicalRequest {
    private String firstname;
    private String lastname;
    private Specialties specialty;

    public SurgicalRequest(String firstname,
                           String lastname,
                           Specialties specialty)
    {
        this.firstname = firstname;
        this.lastname = lastname;
        this.specialty = specialty;
    }

    public String getFirstname() {
        return firstname;
    }

    public Specialties getSpecialty() {
        return specialty;
    }

    public String getLastname() {
        return lastname;
    }
}

A hospital has many surgeons with different specialties as stated earlier. We’ll start with an abstract class named Surgeon.

Here’s the code for the Surgeon Class:

public abstract class Surgeon {

    protected Surgeon nextSurgeon;

    public void setNext(Surgeon next){
        this.nextSurgeon = next;
    }

    public abstract void operate(SurgicalRequest request);
}

Now that we have the base class, we can define surgical procedures before creating our chain. For brevity, we’ll only create three classes for our chain.

Let’s create a class for a vascular surgeon. Here’s the code:

public class VascularSurgeon extends Surgeon {
    @Override
    public void operate(SurgicalRequest request) {
        Specialties specialty = request.getSpecialty();
        if (specialty == Specialties.Arteries
        || specialty == Specialties.Veins)
        {
            System.out.println(
                    String.format("The %s will operate on %s %s. Request approved",
                            this.getClass().getSimpleName(),
                            request.getFirstname(),
                            request.getLastname())
            );
        }
        else{
            if(nextSurgeon != null){
                System.out.println(
                        String.format("%s cannot operate on patient %s %s. Passing request to %s...",
                                this.getClass().getSimpleName(),
                                request.getFirstname(),
                                request.getLastname(),
                                nextSurgeon.getClass().getSimpleName())
                );

                nextSurgeon.operate(request);
            }
            else{
                System.out.println(
                        String.format("Could not find a surgeon who specializes in %s. Request rejected.",
                                request.getSpecialty()));
            }
        }

    }
}

Let’s create another class for a cardiothoracic surgeon. Here’s the code.

public class CardiothoracicSurgeon extends Surgeon {
    @Override
    public void operate(SurgicalRequest request) {
        Specialties specialty = request.getSpecialty();
        if (specialty == Specialties.Heart
                || specialty == Specialties.Lungs
                || specialty == Specialties.Chest
                || specialty == Specialties.Oesophagus)
        {
            System.out.println(
                    String.format("The %s will operate on %s %s. Request approved",
                            this.getClass().getSimpleName(),
                            request.getFirstname(),
                            request.getLastname())
            );
        }
        else{
            if(nextSurgeon != null){
                System.out.println(
                        String.format("%s cannot operate on patient %s %s. Passing request to %s...",
                                this.getClass().getSimpleName(),
                                request.getFirstname(),
                                request.getLastname(),
                                nextSurgeon.getClass().getSimpleName())
                );

                nextSurgeon.operate(request);
            }
            else{
                System.out.println(
                        String.format("Could not find a surgeon who specializes in %s. Request rejected.",
                                request.getSpecialty()));
            }
        }

    }
}

Let’s create just one more class for an Otolaryngology surgeon. Here’s the code:

public class OtolaryngologySurgeon extends Surgeon {
    @Override
    public void operate(SurgicalRequest request) {
        Specialties specialty = request.getSpecialty();
        if (specialty == Specialties.Head
                || specialty == Specialties.Neck
                || specialty == Specialties.Voice
                || specialty == Specialties.Ear
                || specialty == Specialties.Nose)
        {
            System.out.println(
                String.format("The %s will operate on %s %s. Request approved",
                        this.getClass().getSimpleName(),
                        request.getFirstname(),
                        request.getLastname())
            );
        }
        else{
            if(nextSurgeon != null){
                System.out.println(
                        String.format("%s cannot operate on patient %s %s. Passing request to %s...",
                                this.getClass().getSimpleName(),
                                request.getFirstname(),
                                request.getLastname(),
                                nextSurgeon.getClass().getSimpleName())
                );

                nextSurgeon.operate(request);
            }
            else{
                System.out.println(
                        String.format("Could not find a surgeon who specializes in %s. Request rejected.",
                                request.getSpecialty()));
            }
        }
    }
}

Now that we have our objects, we can create the chain. We’ll create the chain by first creating an object for each type of surgeon, then set the next object in the chain for each object. Then we’ll create two requests. One can be performed by one of the available surgeons and the other cannot. This will help to demonstrate both when a request is approved and rejected by the chain.

Here’s the code in the main method:

public static void main(String[] args) {

    OtolaryngologySurgeon otolarynS = new OtolaryngologySurgeon();
    VascularSurgeon vacularS = new VascularSurgeon();
    CardiothoracicSurgeon cardioS = new CardiothoracicSurgeon();

    //chain
    otolarynS.setNext(vacularS);
    vacularS.setNext(cardioS);

    SurgicalRequest heartRequest = new SurgicalRequest(
            "Jackie",
            "Chan",
            Specialties.Heart
    );

    System.out.println("\n-----heart request-----");
    otolarynS.operate(heartRequest);

    SurgicalRequest bladderRequest = new SurgicalRequest(
            "Jet",
            "Lee",
            Specialties.Bladder
    );

    System.out.println("\n-----bladder request-----");
    otolarynS.operate(bladderRequest);

}

The output looks like this:

-----heart request-----
OtolaryngologySurgeon cannot operate on patient Jackie Chan. Passing request to VascularSurgeon...
VascularSurgeon cannot operate on patient Jackie Chan. Passing request to CardiothoracicSurgeon...
The CardiothoracicSurgeon will operate on Jackie Chan. Request approved
-----bladder request-----
OtolaryngologySurgeon cannot operate on patient Jet Lee. Passing request to VascularSurgeon...
VascularSurgeon cannot operate on patient Jet Lee. Passing request to CardiothoracicSurgeon...
Could not find a surgeon who specializes in Bladder. Request rejected.

This is a simple implementation of the chain of responsibility pattern in a nutshell. Thank you for making it to the end.

Happy coding.

Previous: Creational Design Pattern: Prototype

Next: Behavioral Design Pattern: Memento

Programming
Software Development
Software Engineering
Java
Design
Recommended from ReadMedium