Open In App

Null Object Design Pattern

The Null Object Design Pattern is a behavioral design pattern that is used to provide a consistent way of handling null or non-existing objects. It is particularly useful in situations where you want to avoid explicit null checks and provide a default behavior for objects that may not exist.



What is Null Object Design Pattern?

The Null object pattern is a design pattern that simplifies the use of dependencies that can be undefined. This is achieved by using instances of a concrete class that implements a known interface, instead of null references. We create an abstract class specifying various operations to be done, concrete classes extending this class, and a null object class providing do-nothing implementation of this class which will be used seamlessly where we need to check the null value.



Components of Null Object Design Pattern

1. Client

The client is the code that depends on an object that implements a Dependency interface or extends a DependencyBase abstract class. The client uses this object to perform some operation. The client should be able to treat both real and null objects uniformly, without needing to know which type of object it is dealing with.

2. DependencyBase (or Abstract Dependency)

DependencyBase is an abstract class or interface that declares the methods that all concrete dependencies, including the null object, must implement. This class defines the contract that all dependencies must adhere to.

3. Dependency(or Real Dependency)

This class is a functional dependency that may be used by the Client. The client interacts with Dependency objects without needing to know whether they are real or null objects.

4. NullObject(or Null Dependency)

This is the null object class that can be used as a dependency by the Client. It contains no functionality but implements all of the members defined by the DependencyBase abstract class. NullObject represents a null or non-existing dependency in the system. The client can safely call methods on a NullObject without causing errors or needing to perform null checks.

Real-World Analogy of Null Object Design Pattern

Problem Statement:

Consider a car rental service where customers can choose to rent different types of cars.

Example of Null Object Design Pattern

A car rental service allows customers to rent different types of cars. However, some customers may request a car model that is not available in the rental service’s fleet. When this happens, the rental service needs a way to handle this situation gracefully without causing errors or disruptions to the customer.

How Null Object Design Pattern is helpful?

The Null Object Design Pattern is helpful in this situation because it allows the rental service to provide a “null” car object that can be used in place of a real car object when the requested car model is not available. This way, the customer can still use the car object without encountering null reference exceptions or other errors.

Below is the component wise code of above Example:

1. Abstract Dependency (Car)




interface Car {
    void drive();
    void stop();
}

2. Real Dependency (SUV, Sedan)




class SUV implements Car {
    public void drive() {
        System.out.println("Driving an SUV");
    }
 
    public void stop() {
        System.out.println("Stopping an SUV");
    }
}
 
class Sedan implements Car {
    public void drive() {
        System.out.println("Driving a Sedan");
    }
 
    public void stop() {
        System.out.println("Stopping a Sedan");
    }
}

3. Null Object (NullCar)




class NullCar implements Car {
    public void drive() {
        // Do nothing
    }
 
    public void stop() {
        // Do nothing
    }
}

4. Client (CarRentalService)




class CarRentalService {
    private Car car;
 
    public CarRentalService(Car car) {
        this.car = car;
    }
 
    public void rentCar() {
        car.drive();
        car.stop();
    }
}

Complete Code of above example

Below is the complete code of the above example:




interface Car {
    void drive();
    void stop();
}
 
class SUV implements Car {
    public void drive() {
        System.out.println("Driving an SUV");
    }
 
    public void stop() {
        System.out.println("Stopping an SUV");
    }
}
 
class Sedan implements Car {
    public void drive() {
        System.out.println("Driving a Sedan");
    }
 
    public void stop() {
        System.out.println("Stopping a Sedan");
    }
}
 
class NullCar implements Car {
    public void drive() {
        // Do nothing
    }
 
    public void stop() {
        // Do nothing
    }
}
 
class CarRentalService {
    private Car car;
 
    public CarRentalService(Car car) {
        this.car = car;
    }
 
    public void rentCar() {
        car.drive();
        car.stop();
    }
}
 
public class Main {
    public static void main(String[] args) {
        Car suv = new SUV();
        Car sedan = new Sedan();
        Car nullCar = new NullCar();
 
        CarRentalService rentalService1 = new CarRentalService(suv);
        CarRentalService rentalService2 = new CarRentalService(sedan);
        CarRentalService rentalService3 = new CarRentalService(nullCar);
 
        rentalService1.rentCar(); // Output: Driving an SUV, Stopping an SUV
        rentalService2.rentCar(); // Output: Driving a Sedan, Stopping a Sedan
        rentalService3.rentCar(); // No output
    }
}




Driving an SUV
Stopping an SUV
Driving a Sedan
Stopping a Sedan

Below is the explanation of the above code:

When to use the Null Object Design Pattern

The Null Object Design Pattern is useful in situations where you want to provide a default or no-op implementation of an object’s behavior to avoid null checks and handle null references gracefully. Here are some scenarios where the Null Object Design Pattern can be beneficial:

When not to use the Null Object Design Pattern

There are some cases where the Null Object Design Pattern may not be suitable:


Article Tags :