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

Chapter 4 Basics of Clean C++

Listing 4-6.  The Class’s Name Provides Enough Context Information for the Attribute

class CustomerRepository { private:

unsigned int numberOfIncompleteEntries; // ...

};

“You’re creating a vocabulary, not writing a program. Be a poet for a moment. The simple, the punchy, the easily remembered will be far more effective in the long run than some long name that says it all, but in such a way that no one wants to say it at all.”

—Kent Beck, Smalltalk Best Practice Patterns, 1995

Use Names from the Domain

Maybe you have already heard of software design methodologies like object-oriented analysis and design (OOAD) or domain-driven design (DDD)? OOAD was first described by Peter Coad and Edward Yourdon in the early 1990s. It was one of the first software design methodologies in which the so-called domain of the system to be developed plays a central role. More than 10 years later, Eric Evans coined the term domain-driven design” in his eponymous book from 2004 [Evans04]. Like OOAD, DDD is an approach in the complex object-oriented software development that primarily focuses on the core domain and domain logic.

WHAT IS A DOMAIN?

A domain in the realm of systems and software engineering commonly refers to the subject area—the sphere of knowledge, influence, or activity—in which a system of interest is intended to be used. Some examples of domains are Automotive, Medical, Healthcare, Agriculture, Space and Aviation, Online Shopping, Music Production, Railway-Transportation, Energy Economy, etc.

When a system of interest is operated just in a subarea of a domain, this is called a subdomain. For instance, subdomains of the Medical domain are Intensive Care Medicine, and imaging techniques like radiography or magnetic resonance imaging (MRI).

70

Chapter 4 Basics of Clean C++

Simply put, both methodologies (OOAD and DDD) are about trying to make your software a model of a real-life system by mapping business domain things and concepts into the code. For instance, if the software to be developed will support the business processes in a car rental, then things and concepts of car rental (e.g., rented car, car pool, rentee, rental period, rental confirmation, car usage report, accounting, etc.) should be discoverable in the design of this software. If, on the other hand, the software is developed for a certain area in the aerospace industry, things and concepts from this domain should be reflected in it.

The advantages of such an approach are enormous: the use of terms from the domain facilitates, above all, the communication between the developers and other stakeholders. DDD helps the software development team create a common model between the business and IT stakeholders in the company that the team can use to communicate about the business requirements, data entities, and process models.

A detailed introduction to OOAD and DDD is far beyond the scope of this book. If you are interested, I recommend a good, practice-oriented training to learn these methodologies.

However, it is basically always a very good idea to name modules, classes, and functions in a way that elements and concepts from the application’s domain can be rediscovered. This enables you to communicate software designs as naturally as possible. It will make code more understandable to anyone involved in solving a problem, for example, a tester or a business expert.

Take, for example, the aforementioned car rental. The class that is responsible for the use case of the reservation of a car for a certain customer could be as shown in Listing 4-7.

Listing 4-7.  The Interface of a Use Case Controller Class to Reserve a Car

class ReserveCarUseCaseController { public:

Customer identifyCustomer(const UniqueIdentifier& customerId); CarList getListOfAvailableCars(const Station& atStation,

const RentalPeriod& desiredRentalPeriod) const; ConfirmationOfReservation reserveCar(const UniqueIdentifier& carId,

const RentalPeriod& rentalPeriod) const;

private:

Customer& inquiringCustomer;

};

71

Chapter 4 Basics of Clean C++

Now take a look at all those names used for the class, the methods, the arguments, and return types. They represent things that are typical for the car rental domain. If you read the methods from top to bottom, these are the individual steps that are required to rent a car. This is C++ code, but there is a great chance that nontechnical stakeholders with domain knowledge can also understand it.

Note  Software developers should speak the language of their stakeholders and use domain-specific terms in their code whenever possible.

Choose Names at an Appropriate Level of Abstraction

To keep the complexity of today’s software systems under control, these systems are usually hierarchically decomposed. Hierarchical decomposition of a software system means that the entire problem is broken down and partitioned into smaller parts as subtasks, until developers get the confidence that they can manage these smaller parts. I will deepen this topic again in Chapter 6, when covering modularization of a software system.

With such decomposition, software modules are created at different levels of abstraction: starting from large components or subsystems down to very small building blocks like classes. The task, which a building block at a higher abstraction level fulfills, should be fulfilled by an interaction of the building blocks on the next lower abstraction level.

The abstraction levels introduced by this approach also have an impact on naming. Every time we go one step deeper down the hierarchy, the names of the elements get more concrete.

Imagine a web shop. On the top level there might exist a large component whose single responsibility is to create invoices. This component could have a short and descriptive name like Billing. Usually, this component consists of further smaller components or classes. For instance, one of these smaller modules could be responsible for calculating a discount. Another module could be responsible for creating invoice line items. Thus, good names for these modules could be DiscountCalculator

and LineItemFactory. If we now dive deeper into the decomposition hierarchy, the identifiers for components, classes, and functions or methods become more and more concrete, verbose, and thus also longer. For example, a small method in a class at the deepest level could have a very detailed and elongated name, like calculateReducedValueAddedTax().

72

Chapter 4 Basics of Clean C++

Note  Always choose names that reflect the level of abstraction of the module, class, or (member-) function you are working in. Look to it that all instructions within a function are on the same abstraction level.

Avoid Redundancy When Choosing a Name

It is redundant to pick up a class name or other names that provide a clear context and use them as a part to build the name of a member variable. Listing 4-8 shows an example of this.

Listing 4-8.  Don’t Repeat the Class’s Name in its Attributes

#include <string>

class Movie { private:

std::string movieTitle; // ...

};

Don’t do that! It is an, albeit, only very tiny violation of the DRY principle we discussed in Chapter 3. Instead, just name it title. The member variable is in the namespace of class Movie, so it’s clear without ambiguity whose title is meant: the movie’s title!

Listing 4-9 shows another example of redundancy.

Listing 4-9.  Don’t Include the Attribute’s Type in its Name

#include <string>

class Movie { // ...

private:

std::string stringTitle;

};

73