Java – Open Closed principle in design patterns

cdesign-patternsjavaopen-close

I am bit confused about how Open Closed principle can be applied in real life. Requirement in any business changes over the time. According to Open-Closed principle you should extend the class instead modifying the existing class. To me every time extending a class does not seem to be practical to fulfill the requirement. Let me give an example with train booking system.

In train booking system there will be a Ticket object.There could be different types of tickets Regular Ticket, Concession Ticket etc. Ticket is abstract class and RegularTicket and ConcessionTickets are concrete classes. Since all the tickets will have PrintTicket method which is common, hence it is written in Ticket which is base abstract class. Let's say this worked fine for few months. Now new requirement comes in, which states to change the format of the ticket. May be few more fields are added on the printed ticket or may format is changed. To fulfill this requirement I have following options

  1. Modify the PrintTicket() method in Ticket abstract class. But this will violate Open-Closed principle.
  2. Override the PrintTicket() method in child classes but this will duplicate the printing logic which is violation of DRY(Do not repeat yourself) Principle.

So questions are

  1. How can I satisfy above business requirement without violating the Open/Closed principle.
  2. When the class is supposed to be closed for modification? What are the criteria to consider class is closed for modification? Is it after initial implementation of the class or may be after first deployment in production or may be something else.

Best Answer

I have following options

  • Modify the PrintTicket() method in Ticket abstract class. But this will violate Open-Closed principle.
  • Override the PrintTicket() method in child classes but this will duplicate the printing logic which is violation of DRY(Do not repeat yourself) principle.

This depends on the way the PrintTicket is implemented. If the method needs to consider information from subclasses, it needs to provide a way for them to supply additional information.

Moreover, you can override without repeating your code too. For example, if you call your base class method from the implementation, you avoid repetition:

class ConcessionTicket : Ticket {
    public override string PrintTicket() {
        return $"{base.PrintTicket()} (concession)"
    }
}

How can I satisfy above business requirement without violating the Open/Closed principle?

Template Method pattern supplies a third option: implement PrintTicket in the base class, and rely on derived classes to supply additional details as needed.

Here is an example using your class hierarchy:

abstract class Ticket {
    public string Name {get;}
    public string Train {get;}
    protected virtual string AdditionalDetails() {
        return "";
    }
    public string PrintTicket() {
        return $"{Name} : {Train}{AdditionalDetails()}";
    }
}

class RegularTicket : Ticket {
    ... // Uses the default implementation of AdditionalDetails()
}


class ConcessionTicket : Ticket {
    protected override string AdditionalDetails() {
        return " (concession)";
    }
}

What are the criteria to consider class is closed for modification?

It's not the class that should be closed to modification, but rather the interface of that class (I mean "interface" in the broad sense, i.e. a collection of methods and properties and their behavior, not the language construct). The class hides its implementation, so the owners of the class can modify it at any time, as long as its externally visible behavior remains unchanged.

Is it after initial implementation of the class or may be after first deployment in production or may be something else?

The interface of the class needs to remain closed after the first time that you publish it for external use. Internal-use classes remain open to refactoring forever, because you can find and fix all its usages.

Apart from the simplest cases, it is not practical to expect that your class hierarchy is going to cover all possible usage scenarios after a certain number of refactoring iterations. Additional requirements calling for entirely new methods in the base class come up regularly, so your class stays open to modifications by you forever.

Related Topic