the second and moral principle.

Open for Extension and Closed for Modification

First Thought by Bertrand Meyer, a French author who created the idea of Design By Contract.

In More detailed way: You have modules, classes and functions whose behavior is fixed and all the Extensible behaviors are kept behind an abstract interface.

Let’s take an example to explain this:

void checkOut(Receipt receipt){
 Money total = Money.zero;
 for item: items{
	 total += item.getPrice();
	 receipt.addItem(item);
 }
 Payment p = acceptCash(total);
 receipt.addPayment(p);
}

In the above function, checkout, We are doing sum of the items in receipt and also accepting payment in cash.

But what if the payment needs to be also added for credit cards, paypal or other payment methods.

If we follow above coding example, we should be writing an if statement of switch case statement to handle, but that will violate the OCP principle. And to overcome this, we can try using Inversion of Dependency strategy.

If you follow above Depedency Inversion technique, our checkout module is extensible any payment method we might get in future and so that our checkout module is left untouched and only new payments are added or Extended and not Modified in the existing code.

This means, If we truly achieve OCP in our code base, it means every time we want to add a feature or perform incremental update on the code base, we do so by adding new code and not by changing existing code.

This enables or makes the the code to never get modified ever, and enforces developers to write their best possible and cleanest functions and the code will never rot.

But, Is this possible?

Theritically => Yes Practically => No, or Hard

No matter, how hard we try, there will be certain set of modules that need to be changed which sit on the border line of the customer interaction. Like Below.

OCP is an interesting principle. We should not try to make our entire system to make it conformant to OCP but rather certain set of modules or small components to confirm.

We can say that, difficulty of OCP confirmance is proportional to size of the module.

But what are granular reasons, that makes OCP important?

Let’s look at the below example:

public void printReport(ReportPrinter printer){

	int total = 0;
	int mealExpenses = 0;
	printer.print("Expenses "+ getDate() + "\n");
	for (Expense expense: expenses){
		if (expense.type == BREAKFAST || expense.type == DINNER)
			mealExpenses += expense.amount;
		String name = "TILT";
		switch(expense.type){
			case DINNER: name = "Dinner"; break;
			case BREAKFAST: name = "Breakfast"; break;
			case CAR_RENTAL: name = "Car Rental"; break;
		}
		printer.print(String.format(""%s\t%s\t$.02f\n",
		(
			expense.type== DINNER && expense.amount > 5000 ||
			expense.type==BREAKFAST && expense.amount >1000 ? "X": " ",
			name, expense.amount / 100.0
		)
		));
		total += expense.amount;
		
	}
	printer.print(String.format("\nMeal expenses $.02f", mealExpenses / 100.0));
	printer.print(String.format("\nTotal $%.02f", total / 100.0));
}

Some of the problems in the above code are:

  1. Printing information can be separated
  2. Business logic is tightly coupled with printer, which may face modification
  3. expense switch statement, what if new type of expense is added, modification is required.
  4. pennies are converted to dollars, that is a business logic.

Some of the code smells that we can see here are:

  1. Rigidity: If we need to add new expense type, we need to make code changes at multiple places in the code module
  2. Fragility: There can be many modules which depend on this code and problem with this code makes the other modules break in a fanout way.
  3. Immobility

We can use Inversion of Dependency to extend the expense types.

public abstract class Expense{
	private int amount;
	public Expense(int amount){
		this.amount = amount;
	}
	public int getAmount(){
		return amount;
	}
	abstract boolean isOverage();
	abstract boolean isMeal();
}

and a simple extension for this expense type can be:

public class DinnerExpense extends Expense{
	public DinnerExpense(int amount){
		super(amount);
	}
	boolean isOverage(){
		return getAmount() > 5000;
	}
	boolean isMeal(){
		return true;
	}
}

This way we can save the module from becoming rot, by following OCP in mind

But there is a catch here:

What if:

  1. A time parameter comes in to picture => Expense reporter for Transportation on weekends. This pushes our code to modification again.

This again fails with OCP principle. By abstracting the classes, we can only protect ourselves from changes we can expect and not the anyones that may come in future.

Two preventive measures to keep our best foot forward are:

  1. Big Design Up Front.
  2. Agile Design => Make the simples thing and show it customers, then work on the changes expected by customer in iterations.

It is better to follow both the measures to level needed for the system. We cannot completely miss the big design or agile design. If we do so, then our system will get rot and large percentage of system will violate OCP

In iterations, we can make sure, major fraction of our system is OCP.

So in conclusion: OCP is a Moral Principle, someone should strive for, but not for perfection. This makes OCP one of the peculiar Moral Dependency Principle. Which makes it important yet optional sometimes.