avatarFarhad Malik

Summary

The article discusses how to implement an application in Python that is future proof, scalable, easier to update, support, and maintain using well-tested design patterns.

Abstract

The article aims to explain how to implement an application in Python that is future proof, scalable, easier to update, support, and maintain. The author emphasizes that Python is not just a prototyping language, and a Python application can be taken to production as long as it is implemented with the right software principles. The article focuses on using well-tested design patterns to implement a Python application. The author plans to explain the following design patterns with real-life FinTech use cases: Builder, Template, Strategy, and Decorator.

Opinions

  • The author believes that using design patterns encourages clean, easy to understand, and testable code.
  • The author suggests that design patterns can help developers learn business terminology faster and write code that is easier to read and understand.
  • The author thinks that using design patterns can help developers avoid multiple constructors with optional parameters and create a domain-specific language.
  • The author recommends using the Builder design pattern for creating complex objects with a large number of mandatory and optional properties.
  • The author suggests using the Template design pattern for implementing algorithms with a number of steps or instructions that need to be executed in a specific order.
  • The author recommends using the Strategy design pattern for implementing algorithms with a number of methodologies available to solve a problem and for extending an object without creating a large number of subclasses.
  • The author suggests using the Decorator design pattern for adding functionality to an object without changing itself or other objects and for extending an object without creating a large number of subclasses.

How To Build Future Proof Applications?

Programming Python Using Design Patterns

This article aims to explain how we can implement an application in Python that is future proof, scalable, easier to update, support and maintain.

But Python is just a Prototype language; we hear many people say! The reality is far from it. You can take Python application to your Production environment as long as it is implemented and has followed the right software principles.

I want to elaborate on how we can use well-tested design patterns to implement a Python application.

What Is A Design Pattern?

Design pattern is a set of design steps which can be followed to solve reoccurring problems that the software experts have resolved in the past.

There are three categories of design patterns:

Design patterns encourage clean, easy to understand and testable code.

Use Design Patterns To Implement An Application With Long Life

I will be explaining following design patterns with real-life FinTech use cases:

  1. Builder
  2. Template
  3. Strategy
  4. Decorator

I will be presenting the real-life FinTech problems that the design patterns can solve.

Photo by Alex Machado on Unsplash

1. Let’s Understand Builder Design Pattern

1.1 What Is The Builder Design Pattern?

  • Builder design pattern is used to build complex objects (usually a composite).
  • It separates the construction of a complex object from its representation.
  • It is one of the most used creational design patterns.
  • It encourages developers to create a domain specific language.
  • Furthermore, it helps developers learn business terminology faster.

If your object has a number of mandatory and optional properties and its creation is complex then use builder design pattern instead of creating many constructors, or a

constructor with many optional parameters

Photo by Josue Isai Ramos Figueroa on Unsplash

1.2 Builder Design Pattern Use cases

Use builder design pattern if:

  • Your application can have multiple representations of a complex object
  • You want to construct a complex object in steps
  • You want a developer friendly API to construct a complex object
  • You want to build an object in an order and only want to use the object once it is finished building
  • If you intend to write domain driven programming
  • If you want to write code that’s easier to read and understand
  • If you want to avoid multiple constructors with optional parameters

1.3 Builder Design Pattern Example

This is a hypothetical example to illustrate how builder pattern works:

Let’s consider:

  1. We are implementing a trade entry application for a hedge fund. Traders are planning to use the application to manage bond trades.
  2. We implement a Bond class to represent a bond trade in our system.
  3. Each bond object has a large number of properties; a mix of mandatory and optional properties.
  4. Let’s assume that the following properties are mandatory, implying that a bond trade is incomplete without these 5 properties:
  • Maturity Date
  • Start Date
  • Coupon
  • Yield Rate
  • Notional

Additionally, following properties of a bond class are optional:

  • Bond Holder name
  • Bond Issuer Name
Photo by Nazarii Yurkov on Unsplash

1.4 Scenario

We are required to create following 3 bonds:

  • Bond A — with only mandatory properties
  • Bond B — with all mandatory properties + issuer name property populated
  • Bond C — with all mandatory properties + both optional issuer and holder name properties populated

Instead of passing all mandatory and optional properties via constructor every where in the code, use builder design pattern.

Question: How Should We Implement It?

Use Builder Design Pattern

Photo by Chris Ried on Unsplash

1.4 Implementation Steps

1.4 A. Create Bond Class

  1. Create a constructor in bond class which sets the five mandatory properties:
class Bond:
    
    def __init__(self, MaturityDate, StartDate, Coupon, YieldRate, Notional):
        self.__MaturityDate = MaturityDate
        self.__StartDate = StartDate
        self.__Coupon = Coupon
        self.__YieldRate = YieldRate
        self.__Notional = Notional
        self.__BondHolderName = None
        self.__BondIssuerName = None

1.4 B. Create a Bond Builder class: The sole responsibility of your bond builder class is to build a bond object in the right order by:

  • Initializing bond with all of its mandatory properties
  • Allowing callers to set the bond’s optional properties
  • Finally build_bond() method needs to return a completely built bond object.

Each method of BondBuilder class should return self (The Bond Builder Class). This will then provide us with a FluentApi feel.

The build_bond() method should return the actual fully built bond object.

You can only instantiate bond class in BondBuilder’s build_bond() method once all required properties are set.

class BondBuilder:
    def initialise_bond(self, MaturityDate, StartDate, Coupon, YieldRate, Notional):
        self.__my_bond = Bond(MaturityDate, StartDate, Coupon, YieldRate, Notional)
        return self
    
    def set_issuer_name(self, Isser_Name):
        self.__my_bond.__Isser_Name = Isser_Name
        return self
    
    def set_bond_holder_name(self, BondHolderName):
        self.__my_bond.__BondHolderName = BondHolderName
        return self
    
    def build_bond():
        #validate if self.__my_bond is NONE then raise error
        return self.__my_bond

The code below illustrates how we can build different variations of bonds using builder pattern to meet the goal we set for ourselves above.

The fluent API makes the code readable.

BondA = BondBuilder()
                    .initialise_bond(MaturityDate, StartDate, Coupon, YieldRate, Notional)
                    .build_bond()
BondB = BondBuilder()
                    .initialise_bond(MaturityDate, StartDate, Coupon, YieldRate, Notional)
                    .set_bond_holder_name(BondHolderName)
                    .build_bond()
BondC = BondBuilder()
                    .initialise_bond(MaturityDate, StartDate,         Coupon, YieldRate, Notional)
                    .set_bond_holder_name(BondHolderName)
                    .set_issuer_name(Isser_Name)
                    .build_bond()

If we need to create different variations of bond with more optional or mandatory properties then we can simple extend our bond builder class.

Photo by Henry & Co. on Unsplash

1.5 Builder Pattern Summary

  1. Builder pattern makes your code easier to test, maintain and understand.
  2. We can use builder pattern to make code thread-safe. To elaborate, if we make bond class immutable by ensuring all properties are read-only and can only be set via constructor then we can use Builder design pattern to ensure that the state of bond object cannot be changed.
  3. We can then share bond objects in our application without facing any race-conditions due to bond objects.
  4. It gives you a nice to use fluent API that can help you understand domain better.
Photo by Anthony Ginsbrook on Unsplash

If you want to understand what bond financial product is then please read:

2. Let’s Understand Template Design Pattern

2.1 What Is The Template Design Pattern?

Template design pattern is one of the most used patterns in software applications.

Template design pattern is:

  • A common behavioral software development pattern.
  • It ensures your program follows a prescribed behavior.
  • If we want to implement an algorithm that contains a number of steps/instructions then we can use template software design pattern.

If the behaviour of steps within an algorithm can vary yet we want to enforce that the steps of the algorithm are executed in a sequence then use template design pattern.

Photo by Louis Hansel on Unsplash

2.2 Template Design Pattern Use cases

  • Template design pattern is used to encourage clean design and code re-usability.
  • The template base class outlines steps of an algorithm.
  • It ensures that the steps are executed in the right order.
  • The steps are hidden from the outside caller.

Template pattern ensures that the code is clean, follows SOLID principles as each class has a single responsibility and can be easily tested.

Photo by Samuel Zeller on Unsplash

2.3 Scenario

Let’s assume we are working in a FinTech data science application. We are required to use the test statistics algorithm to validate our hypothesis. These algorithms contain following sequence of steps:

  1. State Your Expected Claim
  2. Determine Your Significance Level
  3. Calculate Observed Results
  4. Calculate Test Statistic
  5. State Actual Claim.

There are different variations of test statistics algorithm. As an instance, one can perform T-Test or Z-Test on a hypothesis.

The task is to implement T and Z Test algorithm and ensure that all of the steps are identical, except Step 4 Calculate Test Statistic.

Question: How Should We Implement It?

Use Template Design Pattern

Photo by Dương Trần Quốc on Unsplash
  1. We are going to code test statistics algorithm using template design pattern.
  2. We will code two variations of the algorithm to demonstrate that the template design pattern is a suitable candidate for this problem.
  3. To avoid duplication of code, we are going to implement a skeleton of steps in the right order in an abstract class.

2.4 Implementation Steps

In this section, I will outline basic structure of template design pattern and then I will further explain the concepts via examples:

  1. Base class: Template or skeleton of an algorithm is defined in an abstract base class. The skeleton outlines steps of an algorithm. Base class is implemented to prevent duplication of code and it encourages code re-usability.
  2. Sub-classes: Variations of algorithm are implemented in the sub-classes. The sub-classes override steps of the algorithm and provide their own functionality.
Photo by Kaleidico on Unsplash

Common code is implemented in the base class. Caller invokes the method that then executes the steps within the template in a sequence. Variations of the algorithm are implemented in sub-classes that override the required template methods.

  1. An Abstract Class: BaseTestStatisticProcess class will implement Calculate() method. This method will define template of algorithm which will execute primitive operations. The base class will also contain a protected abstract CalculateTestStatistic() method that the sub-classes will need to override.
  2. Two Concrete Classes: ZTestStatiticProcess and TTestStatisticProcess will be two sub classes. These sub-classes will implement the primitive operations: CalculateTestStatistic().
  3. Each subclass will provide its own implementation of the test statistic.

Coding

This is a sample code to illustrate how template pattern works:

#Template class
import abc
class BaseTestStatisticProcess(metaclass=abc.ABCMeta):
    def StateYourExpectedClaim(self):
        #..put logic here
        pass
    def DetermineYourSignificanceLevel(self):
        #..put logic here
        pass
    def CalculateObservedResults(self):
        #..put logic here
        pass
    @abc.abstractmethod    
    def CalculateTestStatistic(self):
        #..put logic here
        pass
    def StateActualClaim(self):
        #we are going to override it
        pass
    
    #main method
    def Calculate(self):
        self.StateYourExpectedClaim()
        self.DetermineYourSignificanceLevel()
        self.CalculateTestStatistic()
        self.StateActualClaim
        
#Sub classes        
class ZTestStatiticProcess(BaseTestStatisticProcess):
    def CalculateTestStatistic(self):
        #..put specific logic here
        pass
        
class TTestStatiticProcess(BaseTestStatisticProcess):
    def CalculateTestStatistic(self):
        #..put specific logic here
        pass

CalculateTestStatistic is an abstract method and any subclass that implements the abstract class needs to provide implementation for it. This is our template.

The sub classes will automatically execute all of the steps in the right order once the Calculate() method is executed:

ZTestStatiticProcess().Calculate()
TTestStatiticProcess().Calculate()
Photo by Kaleidico on Unsplash

2.5 Template Pattern Summary

  • Implementing algorithms in code is a common task of a software developer.
  • Algorithms are instructions or steps which need to be performed in specific order to achieve desired results.
  • We can use Template Software Design Pattern to implement algorithms.
  • Template design pattern is used to encourage clean design and code re-usability.
  • The template base class outlines steps of an algorithm. It ensures that the steps are executed in the right order.
  • The steps are hidden from the outside caller.
  • Template pattern ensures that the code is clean, follows SOLID principles as each class has a single responsibility and can be easily tested.
Photo by Anomaly on Unsplash

If you want to understand how test statistics work, please read:

3. Let’s Understand Strategy Design Pattern

3.1 What Is The Strategy Design Pattern?

Strategy design pattern is one of the simplest design patterns to understand. It is one of the most famous behavioral software design patterns.

When you have a family of algorithms, all providing common behaviour and you want to change the algorithm without impacting the client code that calls it then you can use strategy pattern.

Photo by Inactive. on Unsplash

3.3 Strategy Design Pattern Use cases

  • A strategy is essentially an algorithm (instructions in code) that is implemented to achieve a specific goal.
  • If there are a number of methodologies (strategies) available to solve a problem then strategy design pattern can be implemented.
  • It enables us to switch implementations for different situations.
  • It also allows us to add new strategies without re-coding and testing all parts of the system.

3.3 Strategy Design Pattern Example

An interest rate swap is a financial product. It can be priced using two strategies: FRA or Bond strategy.

Let’s assume we are implementing a trade pricer application for a Hedge Fund and we are required to price an interest rate swap trade. The analysts have provided us with two strategies and now we need to be in a place to change them interchangeably.

Photo by Startaê Team on Unsplash

3.4 Implementation Steps

There are essentially 3 steps of strategy implementation:

  1. Implement a contract for your algorithm. This will be the interface e.g. ISwapPricer. Interface can contain a public method which you want external callers to execute e.g. PriceSwap(…)
  2. Implement your algorithms in concrete classes. We can implement the two IRS pricing methodologies in the two classes e.g. SwapPricerBondStrategy and SwapPricerFRAStrategy can be the two classes.
  3. Ensure the derived classes implement ISwapPricer interface.
  4. Client of the application can choose and execute appropriate algorithm.
  5. Create an Interest Rate Swap class that will hold all deal specific properties. Pass it to the PriceSwap method of the strategy.
import abc
class ISwapPricer(metaclass=abc.ABCMeta):
    @abc.abstractmethod
    def PriceSwap(trade):
        pass
    
    
class SwapPricerBondStrategy(ISwapPricer):
    def PriceSwap(trade):
        #__price swap how we price two bonds
class SwapPricerFRAStrategy(ISwapPricer):
    def PriceSwap(trade):
        #__price swap how we price Fras

In the future, we can implement more strategies of ISwapPricer without changing the client code.

class Client:
    trade = TradeBuilder.BuildSwapTrade()
    pricers = [SwapPricerBondStrategy(), SwapPricerFRAStrategy()]
    #we can easily switch the algorithms
    for pricer in pricers:
        price = pricer.PriceSwap(trade)

The client code simply gets the pricer to price a swap. Each pricer has its own strategy which has the algorithm that knows how it can price the trade.

Photo by Anastasia Petrova on Unsplash

3.5 Strategy Pattern Summary

  • The section explained that we can price an IRS using bond or FRA strategy. This can be accomplished via strategy design pattern.
  • Client code can interchange strategies without changing all of the code.
  • Makes code easy to test, understand and follows SOLID principles

If you want to understand what an interest rate swap is then please read:

4. Let’s Understand Decorator Design Pattern

4.1 What Is The Decorator Design Pattern?

Decorator design pattern is used to decorate or extend an object’s responsibilities at run time or statically without changing its structure.

Decorator pattern is a widely used structural software design pattern.

Photo by Sylwia Pietruszka on Unsplash

4.2 Decorator Design Pattern Use cases

  • Decorator pattern is an important tool for Open-Closed Principle.
  • Decorator pattern can be used to add functionality to an object without changing itself or other objects.
  • You can use decorator pattern if you want to extend an object without creating a large number of sub classes.
  • Decorator pattern is an alternative to implementing inheritance tree structure.

4.3 Decorator Design Pattern Example

Let’s assume we are implementing a trading platform. An option trader groups a number of option trades and implements a strategy. Sometimes the option strategies are combined to create a new strategy. Each option strategy has its own way of calculating the pay off.

  1. We are required to implement a software application where we can model the new strategies.
  2. Let’s assume we are required to implement a strategy, known as Strangle strategy. This strategy requires the trader to compose options with different strike price.
  3. We are then asked to implement another strategy, which is known as Horizon strategy. This strategy is composed of options with different maturity dates.
  4. Now the trader has requested us to extend the program where he/she could combine any of these two option strategies to create his/her own custom strategy.
  5. Furthermore, we have been informed that the number of strategies and their combinations will grow in the future.
  6. Each strategy has its own way of computing the pay off.

Question: How Should We Implement It?

Use Decorator Design Pattern

Photo by Hannah Busing on Unsplash

4.4 Implementation Steps

  1. Create a base class: Strategy; with method: CalculatePayOff()
  2. Create strategy concrete class: StrangleStrategy that provides its own functionality for CalculatePayOff()
  3. HorizonStrategyDecorator class inherits from StrategyDecorator base class and provides its own functionality for CalculatePayOff().

The key to note is that the CalculatePayOff() function of the HorizonStrategyDecorator decorates the pay off and then executes the strategy’s pay off. It is injected with a Strategy in its constructor.

import abc
class Strategy(metaclass=abc.ABCMeta):
    @abc.abstractmethod
    def CalculatePayOff(self):
        print('Inside Strategy.CalculatePayOff')
        pass
class StrangleStrategy(Strategy):
   def CalculatePayOff(self):
       print('Take StrangleStrategy.CalculatePayOff')
       pass
class StrategyDecorator(Strategy):
   
   def __init__(self,strategy_to_decorate):
      self.strategy_to_decorate = strategy_to_decorate
      
   def CalculatePayOff(self):
     return self.strategy_to_decorate.CalculatePayOff()
   
    
class HorizonStrategyDecorator(StrategyDecorator):
   
   def __init__(self,strategy_to_decorate):
      StrategyDecorator.__init__(self,strategy_to_decorate)
   
   def CalculatePayOff(self):
      print('Take HorizonStrategy.CalculatePayOff')
      return self.strategy_to_decorate.CalculatePayOff()

The decorator class has a constructor that takes Strategy object as an argument. The class can now decorate the strategy.

Photo by Jordane Mathieu on Unsplash

Let’s assume that the trader approaches us today and tells us about a new strategy, named Butterfly strategy. Therefore, we implement a decorator for it.

class ButterflyStrategyDecorator(StrategyDecorator):
   
   def __init__(self,strategy_to_decorate):
      StrategyDecorator.__init__(self,strategy_to_decorate)
   
   def CalculatePayOff(self):
      print('Take Butterfly.CalculatePayOff')
      return self.strategy_to_decorate.CalculatePayOff()

The trader then informs us that we are now assigned to create a strategy that is a combination of Strangle, Horizon and Butterfly strategies.

We don’t need to create new classes. We can simply assemble the decorators together.

We can now easily decorate the strategies without creating any new classes:

Strangle = StrangleStrategy()
StrangleHorizon = HorizonStrategyDecorator(Strangle)
ButterflyHorizonStrangleHorizon = ButterflyStrategyDecorator(StrangleHorizon)
ButterflyHorizonStrangleHorizon.CalculatePayOff()

As you can see in the snippet above, we have implemented new strategies and added dynamic functionalities without creating new classes.

This will print:

Take Butterfly.CalculatePayOff
Take HorizonStrategy.CalculatePayOff
Take StrangleStrategy.CalculatePayOff
Photo by Patrick Perkins on Unsplash

4.5 Decorator Pattern Summary

  • Decorator pattern saved us from creating a large number of classes each time we want to add new functionality into our system.
  • We can see that with decorator pattern, we added additional behavior to an object without creating new objects or changing existing objects.
  • We created a combination of strategies without creating any new class.
  • Decorator pattern is an alternative to implementing inheritance tree structure. Additionally, decorator pattern prevents code from growing exponentially with every new requirement.

If you want read up on option strategies, please read:

Article Summary

This article aimed to outline one of the key concepts in a programmer’s life — to use appropriate design patterns. In particular, I wanted to concentrate on Python programming language due to its ease and popularity.

Get to know these design patterns as these are common patterns in the programming world.

I have explained these populate design patterns along with real-life FinTech use cases:

  1. Builder
  2. Template
  3. Strategy
  4. Decorator

Hope it helps

Programming
Python
Fintech
Technology
Software Development
Recommended from ReadMedium