Design Patterns & C++14

I picked up a copy of Head First Design Patterns a few years back and used it to get up to speed with all the software development design patterns outlined in the infamous Gang of Four book Design Patterns: Elements of Reusable Object-Oriented Software. It’s a great introduction to the implementation of each pattern, albeit using pretty contrived examples (Ducks, pizzas, menus, etc.), and I’d definitely recommend it for anyone looking for an easy read on the topic. Of course like most topics, there are an innumerable number of examples littered around the web these days so you can save some cash if you’re not into the whole buying books thing. There are also some heated debates about the value of design patterns (looking at you Singleton) especially when it comes to their needless overuse. Regardless, last week I wanted to brush up on both design patterns and C++14 so I decided to implement the Java examples in the text in C++14 to kill two birds with one stone.

For anyone not familiar with C++14 (or that the C++ spec has even changed since 1998), it is an updated C++ standard designed to improve and extend the core language and libraries. C++ is a notoriously complex language that often leads people to design really terrible implementations because it isn’t obvious how to do it cleanly. It puts a lot of onus on the developer to have intimate knowledge of all the tricks, traps, and various issues with the language. So, along with the updated spec, the granddaddies of the language (Bjarne Stroustrup, Herb Sutter, et al.) have also attempted to come up with a set of clear guidelines to simplify the process of coding in the language. The C++ Core Guidelines, as they’re called, are an ‘open source’ set of guidelines meant to help people ‘Writ[e] Good C++14… By Default’ (a great talk by Herb Sutter btw, definitely worth watching). The 15000+ line document is by no means an easy read, but it serves a go-to guide for determining how to approach some implementation detail.

On to the design patterns! As a brief example of design patterns in C++14, here’s the Decorator pattern in my implementation, annotated with relevant C++ Core Guideline sections:

#include <string>
#include <iostream>
#include <memory>

namespace DecoratorPattern {

	// Pure abstract Beverage class. The original example stored a description
	// string inside the abstract base class, but it is cleaner to leave
	// Beverage as an interface
	class Beverage {
	public:
		// C.121 : If a base class is used as an interface, make it a pure
		// abstract class
		virtual ~Beverage() = default;
		virtual std::string getDescription() const = 0;		
		virtual double cost() const = 0;
	};

	// Concreate classes implementing beverage types
	class Espresso : public Beverage {
	public:
		std::string getDescription() const override
		{
			return "Espresso";
		}

		double cost() const override
		{
			return 1.99;
		}
	};

	class HouseBlend : public Beverage {
	public:
		std::string getDescription() const override
		{
			return "House Blend Coffee";
		}

		double cost() const override
		{
			return 0.89;
		}
	};

	class DarkRoast : public Beverage {
	public:
		std::string getDescription() const override
		{
			return "Dark Roast Coffee";
		}

		double cost() const override
		{
			return 0.99;
		}
	};

	// Since in the original example CondimentDecorator is identical to
	// Beverage, skip the inheritance chain and just alias
	//
	// T.43: Prefer using over typedef for defining aliases
	using CondimentDecorator = Beverage;

	class Mocha : public CondimentDecorator {
	public:
		// C.46: By default, declare single-argument constructors explicit		
		explicit Mocha(std::unique_ptr<Beverage> beverage)
			: beverage{ std::move(beverage) }
		{
		}

		std::string getDescription() const override
		{
			return beverage->getDescription() + ", Mocha";
		}

		double cost() const override
		{
			return 0.2 + beverage->cost();
		}
	private:
		// R.20: Use unique_ptr or shared_ptr to represent ownership
		std::unique_ptr<Beverage> beverage;
	};

	class Whip : public CondimentDecorator {
	public:
		explicit Whip(std::unique_ptr<Beverage> beverage)
			: beverage{ std::move(beverage) }
		{
		}

		std::string getDescription() const override
		{
			return beverage->getDescription() + ", Whip";
		}

		double cost() const override
		{
			return 0.1 + beverage->cost();
		}
	private:
		std::unique_ptr<Beverage> beverage;
	};

	class Soy : public CondimentDecorator {
	public:
		explicit Soy(std::unique_ptr<Beverage> beverage)
			: beverage{ std::move(beverage) }
		{
		}

		std::string getDescription() const override
		{
			return beverage->getDescription() + ", Soy";
		}

		double cost() const override
		{
			return 0.15 + beverage->cost();
		}
	private:
		std::unique_ptr<Beverage> beverage;
	};

	static void main()
	{
		// ES.23: Prefer the {} initializer syntax
		Beverage& beverage = Espresso{};
		std::cout << beverage.getDescription() << " $" << beverage.cost() <<
			std::endl;

		// R.23: Use make_unique() to make unique_ptrs
		std::unique_ptr<Beverage> beverage2 = std::make_unique<DarkRoast>();

		// R.32: Take a unique_ptr<widget> parameter to express that a function
		// assumes ownership of a widget
		beverage2 = std::make_unique<Mocha>(std::move(beverage2));
		beverage2 = std::make_unique<Mocha>(std::move(beverage2));
		beverage2 = std::make_unique<Whip>(std::move(beverage2));
		std::cout << beverage2->getDescription() << " $" << beverage2->cost() <<
			std::endl;

		std::unique_ptr<Beverage> beverage3 = std::make_unique<HouseBlend>();		
		beverage3 = std::make_unique<Soy>(std::move(beverage3));
		beverage3 = std::make_unique<Mocha>(std::move(beverage3));
		beverage3 = std::make_unique<Whip>(std::move(beverage3));
		std::cout << beverage3->getDescription() << " $" << beverage3->cost() <<
			std::endl;
	}
}

In brief, the Decorator pattern essentially creates a linked-list of objects that wrap and extend the previous instance in the chain. Since they are all derived from the same interface and store a reference to their parent, methods can pass data through the Decorator chain using polymorphism. In this contrived example, ingredients of a beverage wrap an instance of a type of coffee, allowing a dynamic number of ingredients to be added to each. By overloading the getDescription() and cost() methods to call the same method in the instance higher up the chain, each instance can add it’s own modifier to the result that is returned. In the real world, this can be used to decorate an input stream with transformations that modify the result of a read operation. For example, one decorator could convert all text to lowercase, another could buffer the input, and so on. This can be seen in Java’s BufferedInputStream which decorates an InputStream and allows the input to be buffered.

One major change in C++11 and C++14 was the introduction of smart pointers - a mechanism to automatically clean up any allocated memory when the pointer goes out of scope. The goal with smart pointers is to eliminate memory leaks by removing the need for explicit deletes. It also emphasizes the concept of memory ownership, something that was easily overlooked when coding in earlier C++ specs. Since we are creating a chain of decorated objects in this case, each object is responsible for managing the memory of its ancestor. Traditionally, this would be accomplished by simply having a raw pointer (Beverage*) stored in each object, and then cleaning up that memory on the Decorator’s destructor. Instead, we have each Decorator explicitly own it’s parent’s memory using a std::unique_ptr a (unique pointer to managed memory) so that it is cleaned up as soon as the Decorator goes out of scope - cleaning up the chain. In order to pass ownership of instances to subsequent Decorators, we use std::move() to indicate that we are handing off the object, as std::unique_ptr can’t be copied. Since we are passing this to the constructor, we are making it explicitly clear that Decorators will manage the underlying memory. If it instead required a raw pointer, it would be unclear without comments in the source code whether the Decorator would go about delete-ing the underlying representation.

The other C++14 feature I leveraged was the use of the new ‘{}’ initializer syntax. The biggest benefit of using it is that it prevents narrowing conversions - floats silently turning into ints, etc. Not a huge deal in this example, but you play it safer by always using them. Plus, as the guidelines point out, “The rules for {} initialization are simpler, more general, less ambiguous, and safer than for other forms of initialization.”.

Updated: