Welcome back to our Solid Principles series. In the last video, we discussed the Single Responsibility Principle or the SRP. We learned how to design classes that focus on doing just one thing and doing it well.
Today, we are going to move to the second principle, the Open-Closed Principle, often abbreviated as OCP. This principle states that a class should be open for extension but closed for modification. In simple terms, OCP is a class we should be able to add new features or behaviors to our code without altering the existing code.
Now, let's return to our FTS hotel management system. And since we have recently inaugurated the hotel, we are offering some initial discounts to attract more customers. At first, we started with a flat $10 discount on every bill. But soon we decided to add a percentage-based discount and later seasonal discounts for festivals.
Sounds like a good strategy, right? But how do we handle this in code? Let's see how this plays out.
To begin, here's how we will implement a simple flat discount in our system. Let's create a class discount calculator and inside of it, let's create a method double calculate flat discount and it is going to accept bill amount like this. And then let's return the bill amount and subtract our $10 discount like this. Now this is gonna work fine for the flat discount. Guests are gonna get $10 off on their total bill and everything runs smoothly.
But soon the hotel introduces a 10% discount. And to handle this we might decide to modify the discount calculator like this. Firstly, let's take the discount type as well in the method parameter string discount type and put this return statement inside a if block saying that we have to return bill amount minus 10 when the discount type is equals flat.
Else if discount type equals percentage we need to return bill amount. Product width 0.9 which is 10% discount. Otherwise simply return bill amount which is no discount. Now in this code we have introduced an if condition to handle both discount types.
This is gonna work but let's think about what happens next. Imagine what will happen if the hotel wants to add seasonal discounts or the membership discounts in future. We'll need to modify this method every time we introduce a new discount type. And for sure this is gonna violate open closed principle because the class is not closed for modification. Every change adds complexity, increases the risk of introducing bugs and makes the system harder to maintain.
So now let's refactor this to follow the open closed principle. So our system is extensible and future proof. And for that we are going to follow a simple plan.
Firstly, we will create a base discount class to define a common structure. And then each discount type will have its own class that is going to extend the base class discount. And then the discount calculator will work with these classes without needing to know their specific details.
Let us now see our plan in action. For that, let's go back to the code. Shift this method for now.
Now first let's create an abstract class discount. Discount that defines a method applyDiscount. It is going to return a double value abstract and it is going to take bill amount which is again double. Now this will act as a blueprint for all the discount types and next we implement the flat discount as its own class. For that let's create a class flat discount and then Discount.
Now let's override the apply discount method. Override and let's return bill amount subtracted with the $10 like this. And now the flat discount class handles the logic for reducing $10 from the bill. Similarly, we create a percentage discount class. Let's simply create another class, percentage discount.
Again, extend discount. And then override apply discount. Let's return bill amount product with 0.9 that is 10% discount. Done.
Now this percentage discount class calculates a 10% discount by multiplying the bill by 0.9. Now, Let's update the discount calculator to work with these discount classes instead of relying on if conditions. So let's just update this parameter with discount discount and take these off and simply return discount dot apply discount with the bill amount.
Here's the beauty of this design. The discount calculator no longer needs to worry about the details of each discount type. It simply calls the apply discount method of the provided discount object. Let's see how we use this setup in our main program.
Let's say that the double bill amount was 100 and then discount flat discount equals new flat discount discount calculator calculator equals new discount calculator and let's console right-line flat discount calculator dot calculate discount and pass in the flat discount object here and the bill amount and similarly create a discount of type %Discount equals new %Discount and console.WriteLine %Discount, calculator %Discount and bill amount. So basically here we are creating different discount objects. Flat discount here.
and %Discount here. And then we are passing these objects to the discount calculator here and here which applies the discount without knowing the specific type. So now that this code has been refactored using the open close principle, let's see how easy it is to add a new discount type.
Suppose the hotel decides to offer a 20% seasonal discount, we are simply going to add a new class, seasonal discount and it is going to to extend discount, overwrite the method, apply discount and return bill amount with 20% discount that should be product of 0.8 like this and done. Could you guys notice how we added a new discount without modifying the existing discount calculator or other code? This is the power of open-closed principle.
Open for extension and closed for modification. Thank you for watching and stay tuned for the next video where we will explore the Liskov substitution principle which ensures smooth collaboration between classes in a hierarchy. Stay tuned.