Chief Architect's Guide to Implementing the Strategy Pattern in a Travel Booking Platform
Uncle suggested use Strategy pattern

Chief Architect's Guide to Implementing the Strategy Pattern in a Travel Booking Platform

As the chief architect at a rapidly growing travel booking platform, you're in charge of designing the navigation feature for the app. Imagine you're working on a platform like Rapido, Uber, or Ola. Initially, the code was structured to use a single class for all path-generating tasks across different modes of transport. This approach seemed efficient at first but soon led to several issues.

Problems with the Initial Design

  1. Code Duplication: Implementing different algorithms or behaviors in multiple places led to repetitive code, increasing the risk of bugs and making maintenance challenging.
  2. Difficulty in Extending Functionality: Adding new features required significant changes to the existing codebase, reducing modularity and maintainability.
  3. Tight Coupling: The core class handling map functionality became tightly coupled with the algorithms, making changes cumbersome and inflexible.
  4. Limited Reusability: Without clear separation, reusing algorithms in different parts of the application or across projects became challenging.
  5. Increased Complexity in Conditional Logic: Selecting the appropriate algorithm involved complex conditional logic, making the code harder to read, understand, and debug.
  6. Reduced Testability: Testing individual algorithms was difficult due to the intertwined nature of the code.
  7. Scalability Issues: The monolithic design made it hard to scale the application as new features were added.
  8. Lack of Clear Responsibility: The lack of defined responsibilities for choosing and executing algorithms led to potential errors and confusion.


The Solution: Strategy Pattern

To address these issues and enhance the code's maintainability and scalability, the Strategy Pattern can be implemented. This pattern belongs to the Behavioral design family and is defined as follows:

"The Strategy Pattern allows you to define a family of algorithms, encapsulate each one in a separate class, and make them interchangeable. This lets the algorithm vary independently from the clients that use it."

Key Components of the Strategy Pattern:

  • Strategy Interface: A common interface for all supported algorithms.
  • Concrete Strategies: Classes that implement the Strategy interface for different algorithms.
  • Context: The class that uses a Strategy object to execute the algorithm.


Applying the Strategy Pattern to Navigation

Using the Strategy Pattern, we can elegantly solve the path navigation problem. We can create a TransportStrategy interface and define separate concrete classes for each type of transport. The client (e.g., TransportationToAirport) then only needs one instance of the interface, automatically delegating the choice of the required transport type based on supplied parameters, such as Bus, Car, or Taxi.

Advantages of the Strategy Pattern:

  1. Behavioral Flexibility: The class's behavior can change dynamically, supporting multiple algorithms or behaviors depending on context or user input.
  2. Encapsulation of Algorithms: Separating algorithms into distinct classes improves code organization, readability, and maintainability.
  3. Elimination of Conditional Statements: By delegating the choice of algorithm to separate classes, complex conditional logic is reduced, making the code cleaner and easier to understand.
  4. Reusability: Strategies can be reused across different contexts and applications, promoting modularity and reducing duplication.
  5. Adherence to SOLID Principles:

  • Single Responsibility Principle: Each strategy class has a single responsibility—implementing a specific algorithm.
  • Open/Closed Principle: The system is open for extension but closed for modification, allowing new strategies to be introduced without altering existing code.

Diagram of Navigation solution using Strategy Pattern

Improvements Achieved with Strategy Pattern

  1. Decoupling of Code:The Strategy Pattern decouples the implementation of algorithms from the client code. This decoupling allows the client to remain unaware of the specific implementation details of the algorithms it uses, reducing dependencies and increasing modularity.
  2. Dynamic Behavior Changes:The pattern enables dynamic switching of behaviors at runtime without altering the context class.
  3. Scalability:Adding new algorithms or modifying existing ones is straightforward with the Strategy Pattern. Developers can introduce new strategy classes without affecting the existing system, making it easier to scale and extend the system.
  4. Code Maintainability:With the Strategy Pattern, changes to an algorithm do not require changes to the context class. This isolation reduces the risk of introducing bugs and makes the codebase more maintainable.
  5. Simplified Client Code: By offloading algorithm-specific logic to strategy classes, the client code becomes simpler and focuses on higher-level concerns. This simplification reduces the complexity of the client class and makes it easier to manage.

I guess these are way too many advantages to ignore . And I guess you too are excited to code this naughty dirty problem on your code editor. So Lets jump into it.


Implementing the Strategy Pattern in Python: A Step-by-Step Guide

Before jumping into the code lets first look at the code which earlier developers wrote initially for the project.

The above code will lead to all the bad things which are mentioned in the article at start.

Now the steps which we need to follow to spot and apply the Strategy pattern on the above code are:-

1. Identify the Algorithm Prone to Changes

The algorithm here involves finding the time required for different modes of transport (walk, bus, taxi). The Customer class currently uses conditional logic to determine which transport's time-finding method to call.

2. Declare the Strategy Interface

Define a common interface for all transport strategies. This interface will declare the method find_time() that each transport mode will implement.


3. Extract Algorithms into Their Own Classes

Each transport mode will now be its own class implementing the TransportStrategy interface. For example:


4. Add a Field in the Context Class for the Strategy Object

The Customer class will store a reference to a TransportStrategy object. The class should work with this strategy object via the strategy interface and provide a setter to change the strategy at runtime.


5. Client Associates Context with a Suitable Strategy

The client code should now set the desired transport strategy for the Customer and use it to find the time required.

Running logic of code to be added in main method

For reference purposes adding the whole code:-

 from abc import ABC, abstractmethod


class TransportStrategy(ABC):
    @abstractmethod
    def find_time(self) -> int:
        pass


class Walk(TransportStrategy):
    def find_time(self) -> int:
        # Returning a default time for now to avoid complexity
        return 25


class Bus(TransportStrategy):
    def find_time(self) -> int:
        # Returning a default time for now to avoid complexity
        return 15


class Taxi(TransportStrategy):
    def find_time(self) -> int:
        # Returning a default time for now to avoid complexity
        return 10


class Customer:
    def __init__(self):
        self.strategy = None

    def set_transport_strategy(self, strategy: TransportStrategy):
        self.strategy = strategy

    def find_time_required(self) -> int:
        if self.strategy is None:
            raise ValueError("Transport strategy not set")
        return self.strategy.find_time()


# Usage
customer = Customer()

# Customer chooses to walk
customer.set_transport_strategy(Walk())
print(customer.find_time_required())  # Output: 25

# Customer chooses to take the bus
customer.set_transport_strategy(Bus())
print(customer.find_time_required())  # Output: 15

# Customer chooses to take a taxi
customer.set_transport_strategy(Taxi())
print(customer.find_time_required())  # Output: 10

        

Summary

  • Context Class (Customer): Holds a reference to a TransportStrategy object and delegates the calculation of the required time to this object.
  • Strategy Interface (TransportStrategy): Declares the method find_time() to be implemented by all strategies.
  • Concrete Strategies (Walk, Bus, Taxi): Implement the TransportStrategy interface, encapsulating specific algorithms.

Implementing the Strategy Pattern makes adding new transport modes easy without altering the Customer class, ensuring a maintainable and flexible system.

谷歌 Uber GoogleMapsReviews Zomato

Ola

要查看或添加评论,请登录

Manjeet Singh的更多文章

  • Understanding Hard Links and Symbolic Links on macOS

    Understanding Hard Links and Symbolic Links on macOS

    On macOS (and Unix-like systems), you can create two types of links to manage files: Hard Links and Symbolic Links…

  • Unlocking the Power of React Hook Form: A Comprehensive Guide

    Unlocking the Power of React Hook Form: A Comprehensive Guide

    In the dynamic world of front-end development, creating efficient and user-friendly forms is a crucial skill. React…

  • Decorator Pattern And It's Importance

    Decorator Pattern And It's Importance

    Ever wondered how while playing Mario Mr. Mario becomes Super Mario after having a mushroom? Ever thought about how…

    2 条评论
  • Proxy Pattern

    Proxy Pattern

    Let us imagine you go to a service centre to get your vehicle repaired their you take your token number from the…

  • CDN and its working

    CDN and its working

    A content delivery network(CDN) is a network of servers spread all acroos the globe. It consists of a group of edge…

社区洞察

其他会员也浏览了