{
"$type": "site.standard.document",
"canonicalUrl": "https://rednafi.com/go/strategy-pattern/",
"description": "Replace inheritance with the Strategy pattern in Go using interfaces. Achieve composable, testable code without class hierarchies.",
"path": "/go/strategy-pattern/",
"publishedAt": "2024-02-17T00:00:00.000Z",
"site": "at://did:plc:fgtm2c26vfcj74rfmeggbyqj/site.standard.publication/3mnl6f7ob462z",
"tags": [
"Go",
"TIL",
"Design Patterns"
],
"textContent": "These days, I don't build hierarchical types through inheritance even when writing languages\nthat support it. Type composition has replaced almost all of my use cases where I would've\nreached for inheritance before.\n\nI've written about how to [escape the template pattern hellscape] and replace that with\n_strategy pattern_ in Python before. While by default, Go saves you from shooting yourself\nin the foot by disallowing inheritance, it wasn't obvious to me how I could apply the\nstrategy pattern to make things more composable and testable.\n\nAlso, often the Go community exhibits a knee-jerk reaction to the word \"pattern,\" even when\nit has nothing to do with OO. However, I feel it's important to use a specific term while\nexplaining a concept, and I'd rather not attempt to relabel a concept when an established\nterm already exists for it.\n\nJust a quick recap: the strategy pattern is a design approach where you can choose from a\nset of methods to solve a problem, each method wrapped in its own class. This way, you can\nswap out these methods easily without messing with the rest of your code, making it simple\nto adjust behaviors on the fly.\n\nLet's say you're writing a display service that prints a message in either plain text or\nJSON formats. Imperatively you could do this:\n\nWhile this is a trivial example, you can see that adding more formats means we'll need to\nextend the conditionals in the display function, and this gets out of hand pretty quickly\nin many real-life situations where you might have a non-trivial amount of cases.\n\nHowever, the biggest reason why the imperative solution isn't ideal is because of how\ndifficult it is to test. Imagine each of the conditionals triggers some expensive side\neffects when the corresponding block runs. How'd you test display then in an isolated\nmanner without mocking the whole universe?\n\nStrategy pattern tells us that each conditional can be converted into a class with one\nmethod. We call these classes strategies. Then, we initialize these strategy classes at\nruntime and explicitly pass the instances to the display function. The function knows how\nto use the strategy instances and executes a specific strategy to print a message in a\nparticular format based on a certain condition.\n\nHere's how you could rewrite the previous example. In the first phase, we'll wrap each\nformatter in a separate class:\n\nHere, the TextFormatter and JsonFormatter classes implement the MessageFormatter\ninterface. This interface requires the downstream classes to implement the output method.\nThe output methods of the respective formatters know how to format and print the messages.\n\nThe display function simply takes a message and a formatter, and calls\nformatter.output(message) without knowing anything about what the formatter does.\n\nFinally, at runtime, you can instantiate the strategy classes and pass them explicitly to\nthe display function as necessary:\n\nNow whenever you need to test the display function, you can just create a fake formatter\nand pass that as an argument. The display function will happily accept any formatter as\nlong as the strategy class satisfies the MessageFormatter interface.\n\nThe same thing can be achieved in a more functional manner and we'll see that in the Go\nexample.\n\nBut Ruby is still primarily an OO language and it has classes. How'd you model the same\nsolution in a language like Go where there's no concept of a class or explicit interface\nimplementation? This wasn't clear to me from the get-go until I started playing with the\nlanguage a little more and digging through some OSS codebases.\n\nTurns out, in Go, you can do the same thing using interfaces and custom types, and with even\nfewer lines of code. Here's how:\n\nAbove, we're defining a Formatter interface that contains only a single method Output.\nThen we define an OutputFunc type that implements the Output method on the function to\nsatisfy the Formatter interface. We could opt in for a struct type here instead of\ndefining a function type but since we don't need to hold any state, a function type keeps\nthings concise.\n\nThe display function will look as follows:\n\nSimilar to the Ruby example, Display intakes a string message and an object of any type\nthat implements the Formatter interface. Next, it calls the Output method on format\nwithout having any knowledge of what that does, achieving polymorphism.\n\nAlso, notice that we aren't handling the \"unknown formatter\" case explicitly because now\nit'll be a compile-time error if an unknown formatter is passed to the caller.\n\nNext, you'll define your strategies and pass them to the Display function as follows:\n\nWe're defining each formatting strategy as a function and casting it to the OutputFunc so\nthat it satisfies the Formatter interface. Then we just pass the message and the strategy\ninstance to the Display function as before. Notice how your data and strategies are also\ndecoupled in this case; one has no knowledge of the existence of the other.\n\nAnd voila, you're done!\n\n_Update: The original Go example used struct types rather than a function type to meet the\nFormatter interface requirements. In this particular case, the function type makes things\nsimpler. However, if your strategy needs to do multiple things, then a struct with multiple\nmethods is probably going to be better._\n\n\n\n\n\n[escape the template pattern hellscape]:\n /python/escape-template-pattern/",
"title": "Strategy pattern in Go"
}