Practical C++ Design: From Programming to Architecture

Practical C++ Design: From Programming to Architecture Singer, Adam B. Apress, New York, 2017.

CSS Terms: D.1.5 Object-Oriented Programming [PRIMARY]; D.2 Software Engineering; D.1 Programming Techniques; D.3.2 C++

Not long before reviewing his book, I taught a class in C++ using Bjarne Stroustrup's book Programming: Principles and Practices Using C++. Stroustrup, in four chapters (out of 26), has the reader build a small calculator including several advanced features. I thought Practical C++ Design would be a great follow-up, as its focus is on building a calculator in C++: I would surely pick up ways to extend the calculator I had built from Singer. But to my surprise, Singer takes a whole book to design a calculator in several ways less fully realized than Stroustrup's: there are no variables, for instance, and no grammar-driven parser.

To be fair to Singer, he does include features Stroustrup does not, such as a complete separation of his calculator engine from the user interface, which enables him to create a two-faced calculator that can appear on the command line or in a GUI. And he shows how to implement a plug-in feature to extend the calculator, also absent in Stroustrup's program. Furthermore, Singer does an excellent job explaining what design patterns he employs to create each of his calculator's features.

The book begins by setting out the "requirements" for the calculator to be built. Even on a small project like this one, explicitly stating what is required may be useful. And here, this serves an additional didactic purpose.

However, one of the "requirements" of the project is that the calculator use Reverse Polish Notation (RPN). Singer assures us that if we just get used to RPN, we'll like it. But several hundred years of mathematical practice suggest that human beings most easily understand mathematical expressions when they are written in the "normal" way, i.e., using infix notation. RPN was created to ease the burden on programmers and machines, not on users.

After laying out the user requirements, Singer explains his design requirements. The difference between the two is that the user requirements delineate what the software ought to do, while the design requirements describe how it ought to be constructed, in order to do what it needs to do. The chief design virtues Singer wants to illustrate are modularity, encapsulation, cohesion, and low coupling.

Singer next explains the idea of patterns, and differentiates (software) architectural patterns and design patterns. Per his distinction, design patterns apply primarily to a class or a small group of classes, while architectural patterns address entire information processing systems.

The first of the two architectural patterns that Singer examines is multi-tiering, separating a top-tier user interface from a middle, logic tier, and a bottom-level data tier. The second is model-view-controller (MVC). Singer describes the major difference between them: the multi-tier pattern restricts communication in the system to adjacent tiers, while the MVC pattern allows each of M, V, and C to communicate with the others as needed. (Singer winds up choosing MVC for his application.)

In the rest of his progress towards a working calculator, Singer addresses many more important design ideas, including the "pimpl idiom" (a private pointer that keeps a class's private implementation out of a header file), the Observer pattern (for waiting on events), and the Command pattern (for implementing commands like "add" and "subtract"). He discusses modern C++ features such as unique and shared pointers, the `final` keyword, and uniform initialization, and shows how they are superior to older styles used in similar situations.

Due to Singer's attention to modular design and his wise use of design patterns, the `main()` function of the calculator can set up, assemble, and execute the calculator in just six lines. As he notes, the most important aspect of such a design is that a new programmer coming along to maintain the program can easily grasp its overall structure.

This excellent book clearly demonstrates how to employ many OOP design patterns to create a maintainable, working program. I only wish Singer had taken note of his illustrious predecessor's work in the same domain, as he could have shown us how to extend Stroustrup's calculator, instead of starting over and thus omitting key features that Stroustrup had already shown us how to build.