CodeX
Published in

CodeX

Design Patterns | Way to understand LLD

If you are currently trying to prepare for Amazon, Microsoft, basically big tech, there are namely three topics which you have to prepare in order to clear. Algo & Data Structures which everybody knows how important that is. System Design, in other words, HLD(High-Level Design) for which I will highly recommend this book by Alex Xu. Simple and well-done explanations. And Design Patterns aka LLD(low-level design). Through these blogs, I will try to convey what I understand with Design Patterns, and hopefully, it will help the readers. I hope you guys are familiar with Python and its classes. Let’s start!

What is a Design Pattern?

In simple words, you can structure your code based on a general solution to a general problem. These general solutions are known as Design Patterns. Design Patterns are an accumulation of various software developers' experiences. What they feel is the best way to deal with a problem. Design Patterns are various in numbers but we are gonna talk only about 10 of the most important ones in my upcoming articles. Let’s start with the strategy pattern.

The Strategy Pattern

Let’s start with an example. It will help me make you understand the concept a little better.

You buy something and now you want it to get delivered. They are many companies that do that with different cost charges. What we want is to have a single interface that will deal with these different shippers and calculate shipping costs with each of them. When we read these kinds of problems the first thing that comes up in our mind is if/else statements. If the shipper is UPS we will do this else we will do that etc etc. Knowing strategy patterns will power up your thinking and get rid of these primitive if/else statements.

So there are various shippers like Federal Express, UPS, Postal Services and there could be many in the future. So our solution should be extensible also.

First, we will create an Order class.

class Order:
def __init__(self):
pass

Notice our Order class doesn’t contain anything. It’s a principle of Strategy Pattern to give single responsibility to a particular class. Our Shipping Cost class will not depend on the Order class for anything. They perform two different functions.

Now we are gonna define our ShippingCost class which will serve as a context for this problem. The cost of shipping will be calculated by it.

class ShippingCost:
def __init__(self, strategy):
self._strategy = strategy
def shipping_cost(self, order):
return self._strategy.calculate(order)

It’s take in a strategy from various shippers. And calculate cost based on that.

In order to have different logic for different shippers we gotta have a common interface on the basis of which we will structure our code so that when we give some input to our class, the same kind of output will come out of it. To implement the interface in Python we use ABC or abstract base class.

from abc import ABCMeta, abstractmethodclass AbsStrategy:
__metaclass__ = ABCMeta
@abstractmethod
def calculate(self, order):
pass

What our interface tells is that every class which will inherit from it must have calculate method in their definition.

We will then define the logic/algo for our different shippers in their respective classes which are based on our interface.

class FedExStrategy(AbsStrategy):
def calculate(self, order):
return 3.00
class PostalStrategy(AbsStrategy):
def calculate(self, order):
return 5.00
class UPSStrategy(AbsStrategy):
def calculate(self, order):
return 4.00

You can see how this is extensible. If you want to add another shipper you just have to add its own class. You do not have to make changes in our context i.e ShippingCost or our interface or use if/else anywhere and just like that we have taken care of the Open/Closed principle.

Driver Code

# Test FedX servicesorder = Order()
strategy = FedExStrategy()
cost_calculator = ShippingCost(strategy)
cost = cost_calculator.shipping_cost(order)
assert cost == 3.00
# Test UPS shippingorder = Order()
strategy = UPSStrategy()
cost_calculator = ShippingCost(strategy)
cost = cost_calculator.shipping_cost(order)
assert cost == 4.00
# Test POST shippingorder = Order()
strategy = PostalStrategy()
cost_calculator = ShippingCost(strategy)
cost = cost_calculator.shipping_cost(order)
assert cost == 5.00
print("All test passed\n\n\n")

You can see how different cost is calculated just with a different kind of strategy or policy if you will say. That is why this pattern is also called the Policy pattern.

Different kinds of logic/algos are encapsulated and can be used interchangeably as evident by strategy variable in our driver code.

ShippingCost class will take a strategy and call in their calculate method from its own private method “shipping_cost”.

You can copy the above code in one file. Go through it and run it.

This ends our discussion on the Strategy Pattern. Hopefully, you can see the usefulness of this and use it to better your code structure and impress everyone! Till next time.

--

--

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store