Applying the Strategy Pattern to Get Prices from Different Sources in Go
I keep searching for concrete examples in my quest to convince you that Go is a great language for business. I found in finance a great source of ideas to prove my point. I already explored this subject twice (1 and 2) and today I’m going to explore it again.
The strategy pattern is one of the simplest yet underutilized design patterns out there. It is useful when we have one goal and several ways to achieve that. In this case, our goal is to get stock prices from different stock exchanges. Each stock exchange requires a dedicated strategy to fetch the prices since they have different APIs and abstractions. The advantage of this approach is that if we want to support a new stock exchange all we have to do is to add a new strategy and barely change anywhere else. The code that calls the strategies don’t change, which also prevents breaking exising tests and introducing unexpected bugs.
Formally speaking:
“The Strategy Pattern is a behavioral pattern that defines a family of algorithms, encapsulates each one, and makes them interchangeable. Strategy lets the algorithm vary independently from clients that use it.”
Calling the Strategy Pattern Implementation
We start the implementation with constants that represent the supported stock exchanges and a struct to represent the stocks:
Concerning the attributes, the Exchange
is one of those constants declared above. The Ticker
is an acronym that represents the stock. The Name
helps to decode the ticker. The Price
is what we are looking for, so it will be filled later. The following main()
function has a list of pointers to instances of Stock. Yes, we are working with the same instances from the begining to the end. You may not like the mutability aspect of it, but it will help to keep the order of the original list unchanged. I honestly think this is a responsible case of multability because it is reducing accidental complexityhere.
The main()
function is the entry point of the application, working like a controller. It calls the function getPrices()
, which is like a service function, and passes a list of stocks. The function is expected to return the same list with the prices defined. In the sequence, the list is printed as an output of the program.
The getPrices()
function counts with the help of two other functions to be consistent with the single responsibility principle. Let’s take a look at them:
The prepareExchangeStrategies()
function groups stocks by exchange to optimize the calls to the exchange pricing services. Notice that it’s using the strategies to hold the stocks (pricing.addStock(stock)
). It illustrates that a strategy can keep some temporary state to be used when convinient.
Now, look at the searchStocks()
function and notice how clean it is. Those few lines of code are calling distinct algorithms doing different things to achieve the same goal: retrieve the prices of the stocks they hold. The ExchangeFactory
struct and the search()
function are shaped according to the strategy pattern, making things cleaner and elegant.
Implemeting the Strategy Pattern
The Pricing
interface is the core of the strategy pattern. It makes the caller think that all strategies look the same, but they actually have unique souls.
We have seem Pricing
behaviours been called from the functions prepareExchangeStrategies()
and searchStocks()
. These behaviors have an implementation per stock exchange.
But the pricing instances are not created from nowhere. The ExchangeFactory
struct and its method GetExchangePricing()
figure out which strategy the caller needs based on the stock exchange. It does the job using a Map with the exchange’s acronym as keys.
Instances of the strategies are kept in memory for the length of the call. They can’t be kept longer because they cache the stocks they work with, as we can see in the following three implementations.
Our goal is to explain the strategy pattern, not fetching prices from stock exchanges. Maybe we can do it in another time, but let’s keep things short. The NasdaqPricing
struct caches Nasdaq’s stocks using a list and the addStock()
method. The cached stocks are then processed in the search()
method.
The method getName()
is used just for friendly error handling messages.
The other two strategies are essentially the same, except for the search()
method implementation.
Here, Go shows a glance of its elegance. It doesn’t require you to explicitly indicate the interfaces your structs implement, like in Java (public class Foo implements Bar
). It is enough to have the same method signatures. Simplicity matters and it makes me love this language more than any other.
A full implementation of this code is available in my Blog Examples Repo.