Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
roth_stephan_clean_c20_sustainable_software_development_patt.pdf
Скачиваний:
29
Добавлен:
27.03.2023
Размер:
7.26 Mб
Скачать

Chapter 9 Design Patterns and Idioms

Additionally, the Clazz::swap() member function now makes it very easy to implement a move constructor:

class Clazz { public:

// ...

Clazz(Clazz&& other) noexcept { swap(other);

}

// ...

};

Of course, the major goal in good class design should be that it is not necessary to implement explicit copy constructors and assignment operators (recall the Rule of Zero). But when you are forced to do it, you should remember the copy-and-swap idiom.

Pointer to Implementation (PIMPL)

The last section of this chapter is dedicated to an idiom with the funny acronym, PIMPL. PIMPL stands for Pointer to Implementation; and the idiom is also known as

Handle Body, the Compilation Firewall, or Cheshire Cat technique (The Cheshire Cat is a fictional character, a grinning cat, from Lewis Carroll’s novel Alice’s Adventures in Wonderland.) It has, by the way, some similarities with the Bridge pattern described in [Gamma95].

The intent of the PIMPL could be formulated as follows:

“Remove compilation dependencies on internal class implementation details by relocating them into a hidden implementation class and thus improve compile times.”

Let’s look at an excerpt from the Customer class, a class that we’ve seen in many examples before. See Listing 9-49.

Listing 9-49.  An Excerpt from the Contents of the Customer.h Header File

#ifndef CUSTOMER_H_ #define CUSTOMER_H_

#include "Address.h" #include "Identifier.h"

447

Chapter 9 Design Patterns and Idioms

#include <string>

class Customer { public:

Customer();

virtual ~Customer() = default; std::string getFullName() const;

void setShippingAddress(const Address& address); // ...

private:

Identifier customerId; std::string forename; std::string surname; Address shippingAddress;

};

#endif /* CUSTOMER_H_ */

Let’s assume that this is a central business entity in our commercial software system and that it is used (#include "Customer.h") by many other classes. When this header file changes, any files that use that file will need to be recompiled, even if only one private member variable is added, renamed, etc.

In order to reduce these recompilations to the absolute minimum, the PIMPL idiom comes in to play.

First we rebuild the class interface of the Customer class, as shown in Listing 9-50.

Listing 9-50.  The Altered Customer.h Header File

#ifndef CUSTOMER_H_ #define CUSTOMER_H_

#include <memory> #include <string>

class Address;

class Customer { public:

Customer();

448

Chapter 9 Design Patterns and Idioms

virtual ~Customer(); std::string getFullName() const;

void setShippingAddress(const Address& address); // ...

private: class Impl;

std::unique_ptr<Impl> impl;

};

#endif /* CUSTOMER_H_ */

It is conspicuous that all previous private member variables, as well as their associated include-directives, have now disappeared. Instead, a forward declaration for a class named Impl, as well as a std::unique_ptr<T> to this forward-declared class, is present.

Let’s look at the corresponding implementation file, shown in Listing 9-51.

Listing 9-51.  The Contents of the Customer.cpp File

#include "Customer.h"

#include "Address.h" #include "Identifier.h"

class Customer::Impl final { public:

std::string getFullName() const;

void setShippingAddress(const Address& address);

private:

Identifier customerId; std::string forename; std::string surname; Address shippingAddress;

};

std::string Customer::Impl::getFullName() const { return forename + " " + surname;

}

449

Chapter 9 Design Patterns and Idioms

void Customer::Impl::setShippingAddress(const Address& address) { shippingAddress = address;

}

// Implementation of class Customer starts here...

Customer::Customer() : impl { std::make_unique<Customer::Impl>() } { }

Customer::~Customer() = default;

std::string Customer::getFullName() const { return impl->getFullName();

}

void Customer::setShippingAddress(const Address& address) { impl->setShippingAddress(address);

}

In the upper part of the implementation file (up to the source code comment), we can see the Customer::Impl class. Everything has been relocated in this class, which formerly was done directly by the Customer class. Here we also find all member variables.

In the lower section (beginning with the comment), we now find the implementation of the Customer class. The constructor creates an instance of Customer::Impl and holds it in the smart pointer impl. As to the rest, any call of the API of the Customer class is delegated to the internal implementation object.

If something has to be changed in the internal implementation in Customer::Impl, the compiler must only compile Customer.h/Customer.cpp, and then the linker can start its work immediately. Such changes do not have an effect on the outside, and a time-­ consuming compilation of the almost entire project is avoided.

450

APPENDIX A

Small UML Guide

“Learn the rules so you know how to break them properly.”

Rule no. 5 of the Dalai Lama’s “18 Rules for Living”

The OMG Unified Modeling Language (OMG UML)1 is a standardized graphical language used to create models of software and other systems. Its main purpose is to enable developers, software architects, and other stakeholders to design, specify, visualize, construct, and document artifacts of a software system. The language supports both the modeling of structures (the building blocks of a software and their relationships), as well as their behavior (how those building blocks interact and collaborate at runtime). Well-crafted UML models support the discussion between

different stakeholders, serve as an aid to clarify requirements and other issues related to the system of interest, and can capture design decisions.

The vocabulary range of the UML is very extensive. The language offers 15 different diagram types for different purposes. However, as with any other language, it is not usually necessary to use all the vocabulary you know in daily communication. The “art of omission” is also essential here. In practice, there is always a limitation on the language elements you need, and a limitation on a few diagram types.

This appendix provides a brief overview of that subset of UML notations that are used in this book. Each UML element is illustrated (syntax) and briefly explained (semantic). The short definition for an element is based on the current UML specification [OMG15], which can be downloaded for free from OMG’s website. An in-­ depth introduction to the Unified Modeling Language should be made with the help of appropriate literature, or by taking a course at a training provider.

1OMG, Unified Modeling Language, and UML are registered trademarks of the Object Management Group, Inc.

451

© Stephan Roth 2021

S. Roth, Clean C++20, https://doi.org/10.1007/978-1-4842-5949-8