Delegation in Java with Example

Introduction

Delegation is an object-oriented design pattern in which an object passes a task to another object instead of performing it itself. This concept allows for polymorphism and code reuse, leading to more maintainable and flexible code. Delegation helps maintain a loosely coupled system, which is easier to maintain and extend.

Table of Contents

  1. What is Delegation?
  2. Benefits of Delegation
  3. Example 1: Ticket Booking System
  4. Example 2: Printers Implementation
  5. Conclusion

1. What is Delegation?

Delegation is a technique in which an object delegates responsibilities to another helper object. Instead of doing the work itself, it passes the task to another object. This helps achieve polymorphism and ensures that tasks are handled by the most appropriate class.

2. Benefits of Delegation

  • Loosely Coupled Code: Delegation reduces the coupling between classes.
  • Code Reusability: Common functionalities can be reused by multiple classes.
  • Enhanced Flexibility: Changes to the delegated class do not affect the delegator class.
  • Improved Maintainability: Easier to maintain and extend as the code is modular.

3. Example 1: Ticket Booking System

Step-by-Step Implementation

Step 1: Create a TravelBooking Interface

interface TravelBooking {
    void bookTicket();
}

The TravelBooking interface defines a single method bookTicket(), which will be implemented by various travel booking classes.

Step 2: TrainBooking Class

class TrainBooking implements TravelBooking {
    @Override
    public void bookTicket() {
        System.out.println("Train ticket booked");
    }
}

The TrainBooking class implements the TravelBooking interface and provides a specific implementation for booking train tickets.

Step 3: AirBooking Class

class AirBooking implements TravelBooking {
    @Override
    public void bookTicket() {
        System.out.println("Flight ticket booked");
    }
}

The AirBooking class implements the TravelBooking interface and provides a specific implementation for booking air tickets.

Step 4: TicketBookingByAgent Class

class TicketBookingByAgent implements TravelBooking {

    private TravelBooking travelBooking;

    public TicketBookingByAgent(TravelBooking travelBooking) {
        this.travelBooking = travelBooking;
    }

    @Override
    public void bookTicket() {
        travelBooking.bookTicket();
    }
}

The TicketBookingByAgent class also implements the TravelBooking interface but delegates the actual booking task to another TravelBooking object. The delegation is achieved via the constructor which accepts a TravelBooking object. The bookTicket() method calls the bookTicket() method of the delegated TravelBooking object.

Step 5: DelegationDemonstration Class

public class DelegationDemonstration {
    public static void main(String[] args) {
        TicketBookingByAgent agent = new TicketBookingByAgent(new TrainBooking());
        agent.bookTicket();  // Output: Train ticket booked

        agent = new TicketBookingByAgent(new AirBooking());
        agent.bookTicket();  // Output: Flight ticket booked
    }
}

In the DelegationDemonstration class, we create an instance of TicketBookingByAgent and pass it different TravelBooking implementations. The bookTicket() method of TicketBookingByAgent delegates the call to the appropriate booking class (either TrainBooking or AirBooking).

How Delegation Works in the Ticket Booking System

In this example, the TicketBookingByAgent class does not handle the actual ticket booking process. Instead, it delegates this responsibility to the classes that implement the TravelBooking interface (TrainBooking and AirBooking). This allows TicketBookingByAgent to dynamically choose which booking implementation to use at runtime, promoting flexibility and code reuse.

4. Example 2: Printers Implementation

Step-by-Step Implementation

Step 1: Printer Interface

public interface Printer {
    void print(String message);
}

The Printer interface defines a single method print(), which will be implemented by various printer classes.

Step 2: CanonPrinter Class

public class CanonPrinter implements Printer {
    @Override
    public void print(String message) {
        System.out.println("Canon Printer: " + message);
    }
}

The CanonPrinter class implements the Printer interface and provides a specific implementation for printing a message.

Step 3: EpsonPrinter Class

public class EpsonPrinter implements Printer {
    @Override
    public void print(String message) {
        System.out.println("Epson Printer: " + message);
    }
}

The EpsonPrinter class implements the Printer interface and provides a specific implementation for printing a message.

Step 4: HpPrinter Class

public class HpPrinter implements Printer {
    @Override
    public void print(String message) {
        System.out.println("HP Printer: " + message);
    }
}

The HpPrinter class implements the Printer interface and provides a specific implementation for printing a message.

Step 5: PrinterController Class

public class PrinterController implements Printer {

    private final Printer printer;

    public PrinterController(Printer printer) {
        this.printer = printer;
    }

    @Override
    public void print(String message) {
        printer.print(message);
    }
}

The PrinterController class also implements the Printer interface but delegates the actual printing task to another Printer object. The delegation is achieved via the constructor which accepts a Printer object. The print() method calls the print() method of the delegated Printer object.

Step 6: App Class to Test Delegation

public class App {

    public static final String MESSAGE_TO_PRINT = "hello world";

    public static void main(String[] args) {
        PrinterController hpPrinterController = new PrinterController(new HpPrinter());
        PrinterController canonPrinterController = new PrinterController(new CanonPrinter());
        PrinterController epsonPrinterController = new PrinterController(new EpsonPrinter());

        hpPrinterController.print(MESSAGE_TO_PRINT);    // Output: HP Printer: hello world
        canonPrinterController.print(MESSAGE_TO_PRINT); // Output: Canon Printer: hello world
        epsonPrinterController.print(MESSAGE_TO_PRINT); // Output: Epson Printer: hello world
    }
}

How Delegation Works in the Printers Implementation

In this example, the PrinterController class does not handle the actual printing process. Instead, it delegates this responsibility to the classes that implement the Printer interface (CanonPrinter, EpsonPrinter, and HpPrinter). This allows PrinterController to dynamically choose which printer implementation to use at runtime, promoting flexibility and code reuse.

5. Conclusion

Delegation is a powerful design pattern that allows objects to delegate tasks to other objects, promoting code reuse and modularity. By using delegation, you can create more flexible and maintainable systems. In this article, we explored two examples: a ticket booking system and a printer implementation, to demonstrate how delegation can be effectively used in Java.

Happy coding!

Comments

  1. class diagram is wrong. +CanonPrinter() in all the different printer class

    ReplyDelete
    Replies
    1. Class diagram is not wrong. The content CononPrinter is typo in all subclasses.You can fix this.

      Delete
  2. This typo has fixed , thanks for informing.

    ReplyDelete

Post a Comment

Leave Comment