By Sai Viswanathan, who doesn’t just breath air, he breathes programming.
In the previous article, we had discussed about the background of Design Patterns, explanation and the types of it. If you remember, we also had seen the 23 patterns which were classified under Creational, Structural and Behavioural types. As mentioned earlier, this is going to be a series of blog that talks about various Design Patterns. Welcome to the world of Design Patterns!
Let’s take an use case in the e-commerce platform to find the estimated delivery date for an order placed. We’ve seen in many e-commerce sites where they commit to show both shipping and delivery date in various parts of their application. Now, we will see what is going behind the scenes in building such a module. Every time when we receive a requirement, it won’t be final and a frozen one. There will be iterations of discussion that takes place between requirements’ giver and gatherer. Let’s see how the discussion and development phase pans out.
Jai and Veer are a part of tech team who gather requirements and eventually develops the software; Thakur is the Technical Lead and Gabbar is the Product Manager. Gabbar addresses the tech team ahead of announcing the new feature.
Gabbar to Tech Team: Guys, we are building an e-commerce platform from the scratch and you are responsible for building a module that deals with order’s delivery timeline. I will provide you the logic to compute both shipping and delivery date for a product when the inventory is available. So, the logic involves the time taken between the inventory location and customer’s shipping address.
Veer: Gabbar, what if inventory goes out of stock?
Gabbar: Veer, considering the time constraint, let’s assume that the inventory unavailablity usecase is out of scope and will be handled outside the service for the initial phase.
Tech team now gets the complete set of requirements from Gabbar. Jai and Veer thought a simple class would satisfy the need and hence went on to design the software, gets the approval from Thakur and begins the development. A month later, they came up with something like this and had shown to Thakur:
Thakur was much happier with the team’s performance, verified the application once and demonstrated to Gabbar. Gabbar too was impressed that the functionality is working as expected. Application has been released and few weeks have passed by.
One fine day on a Monday morning, Gabbar brings up a new requirement. The e-commerce firm already has retail stores in it and the new update is going to be based on that.
Gabbar to Tech Team: It’s been a while meeting you all since last time. Nice catching up, guys! I have an exiciting update for you. Our company is introducing a new feature in app that our customer can also choose one of our retail stores as their order’s shipping address. I’ll share you the computation logic. You guys be prepared with the estimates and let me know the plan once it’s ready.
Our techies, Jai and Veera brainstormed this feature addition with Thakur and understood that shipping date computation remains unchanged but only the delivery date computation has an impact. Initially, they thought of accomodating both the logic in computeDeliveryDate() method in the existing class. But, considering the logic complexity and OOPS paradigms in mind, they decided to go with an Inheritance. And, their design looks like:
Thakur is happy as usual, feature has been released and few weeks have passed by. Before moving further, let’s retrospect here what has happened so far.
Though the application is working fine and the design seems to have no issue, it actually has a flaw. If you see the parent class, it does three things: a) computing shipping date b) computing delivery date c) displaying the dates. Here, sub-class wants to override only a portion of it which is the delivery date computation. Anyways, this has been taken care in the above design but there seems to be no relationship between parent and child class apart from the delivery date computation logic. At this moment, Thakur hasn’t realized this flaw and since the application seems to be working fine, he let it go.
Design Principle: When you sub-class from a parent class, make sure that IS-A relationship holds true between them.
Be it personal/professional, life doesn’t treat you the same way all the times. Now, Gabbar is back with another set of changes and this time it’s going to create a nightmare to tech team.
Gabbar to Tech Team: Guys, I’m back. Hope you remember the initial discussion during the first launch of our application where we decided not to consider inventory unavailability use case during shipping date computation. Now, we want our application to predict delivery timelines more accurately irrespective of the inventory movement. So, let’s build a logic to handle this use case. I will share the necessary details in a week time. Will catch up soon guys!
Jai: Gabbar, Sorry to interrupt. Does the shipping computation logic remain same for this new requirement?
Gabbar: No, Jai. It’s going to be entirely different.
Now, Veer interrupts.
Veer: Does it change based on the delivery mode (home delivery/store pickup) chosen by customer?
Gabbar: Not really. Shipping computation logic is not dependent on the delivery mode but only based on inventory movement. And, vice versa is also true that delivery computation logic is also not dependent on inventory availability.
Gabbar realized that the team understood the feature and leaves the workstation.
Now, looking at this requirement, Jai and Veer immediately thought of adding additional capabilities to the existing layer of Inheritance and the design looks something like this:
Jai and Veer submits this design to Thakur and he immediately reacts to it.
Thakur: I see a potential problem in your design. Could you see it?
Jai and Veer had no clue what went wrong. Meanwhile, Thakur recently came to know that the organization is planning to introduce Try At Home feature which will introduce another mode of delivery and hence more complexity if he continues with the current design approach. Thakur realized that he’s getting into a bigger problem than what he anticipated. He started feeling like he lost both his hands. Thakur continues.
Thakur: Basic thumb rule of any programming design should be code reusability. Your class design failed to achieve this. computeShippingDate() and computeDeliveryDate() method is duplicated in both HomeDelivery and StorePickup classes. So, I cannot approve this design. Come up with something more flexible, reusable and extendable.
Jai and Veer thought of adding another layer of Inheritance which extends HomeDelivery and StorePickup classes so that delivery computation logic can be avoided of duplication. But, shipping logic would still be duplicated. Also, it introduces unnecessary layer of Inheritance which again doesn’t ensure the IS-A relationship between the parent class. So, they are even more confused to go further.
We all have been in a similar situation during our programming days. Now, ask yourselves whether what would you do when you are Jai/Veer? How will you solve the flaw in this design and make the code reusable and flexible? Let’s go back to the drawing room!
I could see a couple of problems in the above design:
By using Inheritance, we are unnecessarily putting ourselves into a situation to override/maintain the piece of code which is not even relevant. Because of this, we had to duplicate shipping and delivery computation logic. Let’s try to replace Inheritance with something else. As we know already, IS-A relationship doesn’t hold true so why can’t we go with HAS-A relationship?
Composition instead of Inheritance. At times, HAS-A relationship can serve better than IS-A relationship.
So, we decided to remove Inheritance and replace with Composition. But, how? To implement this, understanding the following design principle is crucial.
Design Principle: Identify the aspects of your application that vary and separate them from what stays the same.
In the above class design, we understood that computeShippingDate() and computeDeliveryDate() methods changes based on the inventory availability and delivery mode. And, showDates() method remains same whatsoever. So, take out the shipping and delivery logic away from DateCalculator class and add it in the respective Composition class. Let’s see how we can arrive there.
Now, you may add any number of shipping/delivery computation strategies. Your design will be able to cope up with that. What we did here is taking out the moving parts to its respective Composition class and keeping the static part remains unchanged in the DateCalculator class. So, a strategy is being devised based on different usecases and hence the name Strategy Pattern.
When you have a class where some properties remains same and some changes, it needs a strategy to handle the moving parts.
Let’s see how the definition looks like:
The Strategy Pattern defines a family of algorithms, encapsulates each one and makes them interchangeable. Strategy lets the algorithm vary indepently from clients that use it.
Since this design pattern majorly deals with the strategy on how the classes and it’s object should behave, it comes under Behavioral Design Pattern.
I know this blog is quite lengthy but I believe it served the purpose. I thought instead of taking you to the right solution immediately, taking you to the series of inappropriate solution, let you understand the mistakes and then showing the right solution helps. See you in another interesting blog on the next set of Design Patterns! Bye, until then!!
In this blog, I took the characters of the iconic movie Sholay to make the conversation look little interesting but no other intentions.
We at CaratLane are solving some of the most intriguing challenges to make our mark in the relatively uncharted omnichannel jewellery industry. If you are interested in tackling such obstacles, feel free to drop your updated resume/CV to email@example.com!