Strategy Pattern — Part 1 of a design patterns book study

Graham Holtslander
Vendasta
Published in
4 min readMar 11, 2020

Head First Design Patterns is a book by Eric Freeman and Elisabeth Robson which is written with Java developers in mind — the code is all Java (and the book is probably intended to help people get better at Java). While I am not a Java developer, design patterns are a useful thing to know, and the conversational nature of the book is great for retention, I’ve learned a lot!

Photo by Håkon Helberg on Unsplash

This is the first part of a many-part series on design patterns. There are many different design patterns discussed in the book that I (and perhaps some guest authors) will be taking a look at over the next few months. Without further ado, let’s look at the strategy pattern.

This pattern used ducks, which was enjoyable for me on a personal level — my favourite bird for sure, maybe my favourite animal in general, is the mallard duck. The idea with the strategy pattern is that rather than make a subclass for every type and configuration of an object, instead provide different strategies that a smaller set of objects can interact with. So for ducks, there are strategies for making sound and for flying. In my go code I ended up making a Flyer and a Quacker interface (verbs in the classic go style). A Duck then is a struct made up of a combination of the two strategies. Like this:

type Flyer interface {
Fly()
}
type Quacker interface {
Quack()
}
type Duck struct {
fly Flyer
quack Quacker
}
func NewDuck(q Quacker, f Flyer) {
return Duck{quack: q, fly: f}
}

So then some example uses of strategies:

mallard := NewDuck(NewItQuacks(), NewFlyWithWings())
mallard.Quack()
mallard.Fly()

In my case, those methods just print out whatever the strategy enables, so it would print something about quacking and flying respectively. But for a rubber duck, it would use the NewFlyNoWings() (that’s what they had in the book, ha). Thus when trying to make it quack and fly, it would print about quacking, but say it can’t fly. Here is a rubber duck (squeaks, no flying) and a wooden duck (doesn’t quack, doesn’t fly):

rubberDuck := NewDuck(NewItQuacks(), NewFlyNoWings())
woodenDuck := NewDuck(NewSilentQuack(), NewFlyNoWings())

The other big thing about strategy pattern is you can swap strategies at runtime. So we could add some setter methods to our Duck that allow us to update it on the fly (get it?). The example they use is of a model duck that has an optional rocket attachment. Let’s see what that looks like.

func (d *Duck) SetQuacker(q Quacker) {
d.quack = q
}
func (d *Duck) SetFlyer(f Flyer) {
d.fly = f
}
// And our new fly style
type FlyRocketPowered struct{}
func (f FlyRocketPowered) Fly() {
fmt.Println(“Flying with rocket power!”)
}
func NewFlyRocketPowered() Flyer {
return FlyRocketPowered{}
}

Then, in our main function:

modelDuck := NewDuck(NewItQuacks(), NewFlyNoWings())
modelDuck.Quack()
modelDuck.Fly() // No flying :(
modelDuck.SetFlyer(NewFlyRocketPowered())
modelDuck.Fly() // We flyin’!

This book is, naturally, heavily focused on Object-Oriented (OO) programming and principles. That’s kind of the point, from their perspective. One of the principles they introduce in this chapter is that you should encapsulate the behaviours (and code) that change and move them away from the things that stay the same. Earlier on I said that the alternative to a design like what we have now is to have a subclass for every type of duck. So say we have classes like a MallardDuck, RubberDuck, ModelDuck, etc. that all subclass Duck and override functionality. If we need to add or remove functionality, we now need to go into every subclass and change it. By using the strategy pattern and allowing ducks to use different strategies based on what they need we can encapsulate those changes into the strategies, rather than leaving them in the concrete ducks. Speaking of that, ConcreteDuck
would be an interesting class to implement — I’ll leave it as an exercise for the reader.

The biggest takeaway from this pattern: Take what varies and encapsulate it so it won’t affect the rest of your code. A very recent real-world example of this came up just the other day. In some front end code, we needed to
show a different dialog based on which page of the app we were on. We could have checked the URL in the dialog itself and done a bunch of ternary logic to switch what we were showing, or we could encapsulate that change further up and show a totally different dialog based on the page. This allows us to have two separate dialogs that can be developed independently. If we need another dialog someday, we have an easy insertion point now too - we don’t have to add further ternary expressions or switches or anything like that. As with all OO design, it’s a matter of tradeoffs. Yes, it is slightly more code and another class and etc., but the added clarity is worth it in this case.

Thanks for reading, and I hope you look forward to the next part: Observer pattern!

--

--