![](/user_photo/_userpic.png)
- •Table of Contents
- •About the Author
- •About the Technical Reviewer
- •Acknowledgments
- •Software Entropy
- •Clean Code
- •C++11: The Beginning of a New Era
- •Who This Book Is For
- •Conventions Used in This Book
- •Sidebars
- •Notes, Tips, and Warnings
- •Code Samples
- •Coding Style
- •C++ Core Guidelines
- •Companion Website and Source Code Repository
- •UML Diagrams
- •The Need for Testing
- •Unit Tests
- •What About QA?
- •Rules for Good Unit Tests
- •Test Code Quality
- •Unit Test Naming
- •Unit Test Independence
- •One Assertion per Test
- •Independent Initialization of Unit Test Environments
- •Exclude Getters and Setters
- •Exclude Third-Party Code
- •Exclude External Systems
- •What Do We Do with the Database?
- •Don’t Mix Test Code with Production Code
- •Tests Must Run Fast
- •How Do You Find a Test’s Input Data?
- •Equivalence Partitioning
- •Boundary Value Analysis
- •Test Doubles (Fake Objects)
- •What Is a Principle?
- •KISS
- •YAGNI
- •It’s About Knowledge!
- •Building Abstractions Is Sometimes Hard
- •Information Hiding
- •Strong Cohesion
- •Loose Coupling
- •Be Careful with Optimizations
- •Principle of Least Astonishment (PLA)
- •The Boy Scout Rule
- •Collective Code Ownership
- •Good Names
- •Names Should Be Self-Explanatory
- •Use Names from the Domain
- •Choose Names at an Appropriate Level of Abstraction
- •Avoid Redundancy When Choosing a Name
- •Avoid Cryptic Abbreviations
- •Avoid Hungarian Notation and Prefixes
- •Avoid Using the Same Name for Different Purposes
- •Comments
- •Let the Code Tell the Story
- •Do Not Comment Obvious Things
- •Don’t Disable Code with Comments
- •Don’t Write Block Comments
- •Don’t Use Comments to Substitute Version Control
- •The Rare Cases Where Comments Are Useful
- •Documentation Generation from Source Code
- •Functions
- •One Thing, No More!
- •Let Them Be Small
- •“But the Call Time Overhead!”
- •Function Naming
- •Use Intention-Revealing Names
- •Parameters and Return Values
- •Avoid Flag Parameters
- •Avoid Output Parameters
- •Don’t Pass or Return 0 (NULL, nullptr)
- •Strategies for Avoiding Regular Pointers
- •Choose simple object construction on the stack instead of on the heap
- •In a function’s argument list, use (const) references instead of pointers
- •If it is inevitable to deal with a pointer to a resource, use a smart one
- •If an API returns a raw pointer...
- •The Power of const Correctness
- •About Old C-Style in C++ Projects
- •Choose C++ Strings and Streams over Old C-Style char*
- •Use C++ Casts Instead of Old C-Style Casts
- •Avoid Macros
- •Managing Resources
- •Resource Acquisition Is Initialization (RAII)
- •Smart Pointers
- •Unique Ownership with std::unique_ptr<T>
- •Shared Ownership with std::shared_ptr<T>
- •No Ownership, but Secure Access with std::weak_ptr<T>
- •Atomic Smart Pointers
- •Avoid Explicit New and Delete
- •Managing Proprietary Resources
- •We Like to Move It
- •What Are Move Semantics?
- •The Matter with Those lvalues and rvalues
- •rvalue References
- •Don’t Enforce Move Everywhere
- •The Rule of Zero
- •The Compiler Is Your Colleague
- •Automatic Type Deduction
- •Computations During Compile Time
- •Variable Templates
- •Don’t Allow Undefined Behavior
- •Type-Rich Programming
- •Know Your Libraries
- •Take Advantage of <algorithm>
- •Easier Parallelization of Algorithms Since C++17
- •Sorting and Output of a Container
- •More Convenience with Ranges
- •Non-Owning Ranges with Views
- •Comparing Two Sequences
- •Take Advantage of Boost
- •More Libraries That You Should Know About
- •Proper Exception and Error Handling
- •Prevention Is Better Than Aftercare
- •No Exception Safety
- •Basic Exception Safety
- •Strong Exception Safety
- •The No-Throw Guarantee
- •An Exception Is an Exception, Literally!
- •If You Can’t Recover, Get Out Quickly
- •Define User-Specific Exception Types
- •Throw by Value, Catch by const Reference
- •Pay Attention to the Correct Order of Catch Clauses
- •Interface Design
- •Attributes
- •noreturn (since C++11)
- •deprecated (since C++14)
- •nodiscard (since C++17)
- •maybe_unused (since C++17)
- •Concepts: Requirements for Template Arguments
- •The Basics of Modularization
- •Criteria for Finding Modules
- •Focus on the Domain of Your Software
- •Abstraction
- •Choose a Hierarchical Decomposition
- •Single Responsibility Principle (SRP)
- •Single Level of Abstraction (SLA)
- •The Whole Enchilada
- •Object-Orientation
- •Object-Oriented Thinking
- •Principles for Good Class Design
- •Keep Classes Small
- •Open-Closed Principle (OCP)
- •A Short Comparison of Type Erasure Techniques
- •Liskov Substitution Principle (LSP)
- •The Square-Rectangle Dilemma
- •Favor Composition over Inheritance
- •Interface Segregation Principle (ISP)
- •Acyclic Dependency Principle
- •Dependency Inversion Principle (DIP)
- •Don’t Talk to Strangers (The Law of Demeter)
- •Avoid Anemic Classes
- •Tell, Don’t Ask!
- •Avoid Static Class Members
- •Modules
- •The Drawbacks of #include
- •Three Options for Using Modules
- •Include Translation
- •Header Importation
- •Module Importation
- •Separating Interface and Implementation
- •The Impact of Modules
- •What Is Functional Programming?
- •What Is a Function?
- •Pure vs Impure Functions
- •Functional Programming in Modern C++
- •Functional Programming with C++ Templates
- •Function-Like Objects (Functors)
- •Generator
- •Unary Function
- •Predicate
- •Binary Functors
- •Binders and Function Wrappers
- •Lambda Expressions
- •Generic Lambda Expressions (C++14)
- •Lambda Templates (C++20)
- •Higher-Order Functions
- •Map, Filter, and Reduce
- •Filter
- •Reduce (Fold)
- •Fold Expressions in C++17
- •Pipelining with Range Adaptors (C++20)
- •Clean Code in Functional Programming
- •The Drawbacks of Plain Old Unit Testing (POUT)
- •Test-Driven Development as a Game Changer
- •The Workflow of TDD
- •TDD by Example: The Roman Numerals Code Kata
- •Preparations
- •The First Test
- •The Second Test
- •The Third Test and the Tidying Afterward
- •More Sophisticated Tests with a Custom Assertion
- •It’s Time to Clean Up Again
- •Approaching the Finish Line
- •Done!
- •The Advantages of TDD
- •When We Should Not Use TDD
- •TDD Is Not a Replacement for Code Reviews
- •Design Principles vs Design Patterns
- •Some Patterns and When to Use Them
- •Dependency Injection (DI)
- •The Singleton Anti-Pattern
- •Dependency Injection to the Rescue
- •Adapter
- •Strategy
- •Command
- •Command Processor
- •Composite
- •Observer
- •Factories
- •Simple Factory
- •Facade
- •The Money Class
- •Special Case Object (Null Object)
- •What Is an Idiom?
- •Some Useful C++ Idioms
- •The Power of Immutability
- •Substitution Failure Is Not an Error (SFINAE)
- •The Copy-and-Swap Idiom
- •Pointer to Implementation (PIMPL)
- •Structural Modeling
- •Component
- •Interface
- •Association
- •Generalization
- •Dependency
- •Template and Template Binding
- •Behavioral Modeling
- •Activity Diagram
- •Action
- •Control Flow Edge
- •Other Activity Nodes
- •Sequence Diagram
- •Lifeline
- •Message
- •State Diagram
- •State
- •Transitions
- •External Transitions
- •Internal Transitions
- •Trigger
- •Stereotypes
- •Bibliography
- •Index
![](/html/75672/2303/html_3iAzqyDIia.Sav3/htmlconvd-3gbB7Q66x1.jpg)
Chapter 3 Be Principled
Now it’s much easier to change the innards of AutomaticDoor. The client code does not depend on internal parts of the class anymore. You can remove the State enumeration and replace it with another kind of implementation without users of the class noticing this.
Strong Cohesion
A general piece of advice in software development is that any software entity (i.e., module, component, unit, class, function, etc.) should have a strong (or high) cohesion. In very general terms, cohesion is strong when the module does a well-defined job.
To dive deeper into this principle, let’s look at two examples where cohesion is weak, starting with Figure 3-1.
Figure 3-1. MyModule has too many responsibilities, and this leads to many dependencies from and to other modules
53
![](/html/75672/2303/html_3iAzqyDIia.Sav3/htmlconvd-3gbB7Q67x1.jpg)
Chapter 3 Be Principled
In this illustration of the modularization of an arbitrary system, three different aspects of the business domain are placed inside one single module. Aspects A, B, and C have nothing, or nearly nothing, in common, but all three are placed inside MyModule. Looking at the module’s code could reveal that the functions of A, B, and C are operating on different, and completely independent, pieces of data.
Now look at all the dashed arrows in that picture. Each of them is a dependency. The element at the tail of such an arrow requires the element at the head of the arrow for its implementation. In this case, any other module of the system that wants to use services offered by A, or B, or C will make itself dependent from the whole module MyModule. The major drawback of such a design is obvious: it will result in too many dependencies and the maintainability goes down the drain.
To increase cohesion, the aspects of A, B, and C should be separated from each other and moved into their own modules (Figure 3-2).
Figure 3-2. High cohesion: The previously mixed aspects A, B, and C have been separated into discrete modules
54
![](/html/75672/2303/html_3iAzqyDIia.Sav3/htmlconvd-3gbB7Q68x1.jpg)
Chapter 3 Be Principled
Now it is easy to see that each of these modules has far fewer dependencies than the old MyModule. It is clear that A, B, and C have nothing to do with each other directly. The only module that depends on all three modules A, B, and C is Module 1.
Another form of weak cohesion is called the shot gun anti-pattern. I think it is generally known that a shot gun is a firearm that shoots a huge amount of small spherical pellets. The weapon typically has a large scatter. In software development, this metaphor is used to express that a certain domain aspect, or single logical idea, is highly fragmented and distributed across many modules. Figure 3-3 depicts such a situation.
Figure 3-3. Aspect A is scattered over five modules
Even with this form of weak cohesion, many unfavorable dependencies arise. The distributed fragments of Aspect A must work closely together. That means that every module that implements a subset of Aspect A must interact at least with one other module containing another subset of Aspect A. This leads to a large number of
dependencies crosswise through the design. At worst, it can lead to cyclic dependencies, like between Modules 1 and 3, or between Modules 6 and 7. This has, once again, a negative impact on the maintainability and extendibility. Furthermore, the testability is also very poor due to this design.
55
Chapter 3 Be Principled
This kind of design will lead to something that is called shotgun surgery. A certain type of change regarding Aspect A leads to making lots of small changes to many modules. That’s really bad and should be avoided. We have to fix this by pulling all the parts of the code that are fragments of the same logical aspect together into a single cohesive module.
There are certain other principles—for instance, the single responsibility principle (SRP) of object-oriented design (see Chapter 6)—that foster high cohesion. High cohesion often correlates with loose coupling and vice versa.
Loose Coupling
Consider the small example in Listing 3-8.
Listing 3-8. A Switch That Powers a Lamp On and Off
class Lamp { public:
void on() { //...
}
void off() { //...
}
};
class Switch { private:
Lamp& lamp;
bool state {false};
public:
Switch(Lamp& lamp) : lamp(lamp) { }
void toggle() { if (state) {
state = false; lamp.off();
56
![](/html/75672/2303/html_3iAzqyDIia.Sav3/htmlconvd-3gbB7Q70x1.jpg)
Chapter 3 Be Principled
}else {
state = true; lamp.on();
}
}
};
Basically, this piece of code will work. You can first create an instance of the Lamp class. Then this is passed by reference when instantiating the Switch class. Visualized with UML, this small example would look like Figure 3-4.
Figure 3-4. A class diagram of Switch and Lamp
What’s the problem with this design?
The problem is that the Switch contains a direct reference to the concrete class Lamp. In other word, the switch knows that there is a lamp.
Maybe you would argue, “Well, but that’s the purpose of the switch. It has to power on and off lamps.” That’s true if that is the one and only thing the switch should do.
If that’s the case, this design might be adequate. But go to a DIY store and look at the switches that you can buy there. Do they know that lamps exist?
And what do you think about the testability of this design? Can the switch be tested independently as it is required for unit testing? No, this is not possible. And what will we do when the switch has to power on not only a lamp, but also a fan or an electric roller blind?
In this example, the switch and the lamp are tightly coupled.
In software development, a loose coupling (also known as low or weak coupling) between modules is best. That means that you should build a system in which each of its modules has, or makes use of, little or no knowledge of the definitions of other separate modules.
The key to achieve loose coupling in object-oriented software designs is to use interfaces. An interface declares publicly accessible behavioral features of a class without
57
Chapter 3 Be Principled
committing to a particular implementation of that class. An interface is like a contract. Classes that implement an interface are committed to fulfill the contract, that is, these classes must provide implementations for the method signatures of the interface.
In C++, interfaces are implemented using abstract classes, as shown in Listing 3-9.
Listing 3-9. The Switchable Interface
class Switchable { public:
virtual void on() = 0; virtual void off() = 0;
};
The Switch class doesn’t contain a reference to the lamp any more. Instead, it holds a reference to our new interface class called Switchable, as shown in Listing 3-10.
Listing 3-10. The Modified Switch Class, Whereby Lamp Is Gone
class Switch { private:
Switchable& switchable; bool state {false};
public:
Switch(Switchable& switchable) : switchable(switchable) {}
void toggle() { if (state) {
state = false; switchable.off();
} else {
state = true; switchable.on();
}
}
};
58
![](/html/75672/2303/html_3iAzqyDIia.Sav3/htmlconvd-3gbB7Q72x1.jpg)
Chapter 3 Be Principled
The Lamp class implements our new interface, as shown in Listing 3-11.
Listing 3-11. The Lamp Class Implements the Switchable Interface
class Lamp : public Switchable { public:
void on() override { // ...
}
void off() override { // ...
}
};
Expressed in UML, the new design looks like Figure 3-5.
Figure 3-5. Loosely coupled Switch and Lamp via an interface
The advantages of such a design are obvious. Switch is completely independent from concrete classes that will be controlled by it. Furthermore, Switch can be tested independently by providing a test double implementing the Switchable interface. You want to control a fan instead of a lamp? No problem, as this design is open for extension. Just create a Fan class or other classes representing electrical devices that implement the Switchable interface, as depicted in Figure 3-6.
59