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

Chapter 4 Basics of Clean C++

advisable to start with C++14, which was essentially a bugfix release of C++11, or even start with a higher standard right away.

Now let’s explore the key elements of clean and modern C++, step by step.

Good Names

“Programs must be written for people to read, and only incidentally for machines to execute.”

—Hal Abelson and Gerald Jay Sussman, 1984

The following piece of source code is taken from Apache OpenOffice version 3.4.1, a well-­known open source office software suite. Apache OpenOffice has a long history, which dates back to the year 1984. It descends from Oracle’s OpenOffice.org (OOo), which was an open sourced version of the earlier StarOffice. In 2011, Oracle stopped the development of OpenOffice.org, fired all developers, and contributed the code and trademarks to the Apache Software Foundation. Therefore, be tolerant and keep in mind that the Apache Software Foundation has inherited a nearly 30-year-old ancient beast and vast technical debt.

Listing 4-1.  An Excerpt from Apache’s OpenOffice 3.4.1 Source Code

// Building the info struct for single elements SbxInfo* ProcessWrapper::GetInfo( short nIdx )

{

Methods* p = &pMethods[ nIdx ];

//Wenn mal eine Hilfedatei zur Verfuegung steht:

//SbxInfo* pResultInfo = new SbxInfo( Hilfedateiname, p->nHelpId ); SbxInfo* pResultInfo = new SbxInfo;

short nPar = p->nArgs & _ARGSMASK; for( short i = 0; i < nPar; i++ )

{

p++;

String aMethodName( p->pName, RTL_TEXTENCODING_ASCII_US ); sal_uInt16 nInfoFlags = ( p->nArgs >> 8 ) & 0x03;

66

Chapter 4 Basics of Clean C++

if( p->nArgs & _OPT ) nInfoFlags |= SBX_OPTIONAL;

pResultInfo->AddParam( aMethodName, p->eType, nInfoFlags );

}

return pResultInfo;

}

I have a simple question for you: What does this function do?

It seems easy to give an answer at first sight, because the code snippet is small (less than 20 LOC) and the indentation is okay. But in fact, it is not possible to say at a glance what this function really does, and the reason for this is not only the domain of office software, which is possibly unknown to us.

This short code snippet has many bad smells (e.g., commented-out code, comments in German, magic literals like 0x03, etc.), but a major problem is the poor naming. The function’s name GetInfo() is very abstract and gives us at most a vague idea of what this function actually does. Also the namespace name ProcessWrapper is not very helpful. Perhaps you can use this function to retrieve information about a running process? Well, wouldn’t RetrieveProcessInformation() be a much better name for it? The comment on the first line (“Building the info struct...”) indicates that something is created.

After an analysis of the function’s implementation you will also notice that the name is misleading, because GetInfo() is not just a simple getter as you might suspect. There is also something created with the new operator. In other words, the call site

will receive a resource that was allocated on the heap and the caller must take care of it. To emphasize this fact, wouldn’t a name like CreateProcessInformation() or

BuildProcessInfoFromIndex() be much better?

Next, take a look at the parameter and the return value of the function. What is SbxInfo? What is nIdx? Maybe the argument nIdx holds a value that is used to access an element in a data structure (that is, an index), but that would just be a guess. In fact, we don’t know exactly.

Developers very often read source code, usually more often even than they write new code. Therefore, source code should be readable, and good names are a key factor of readability. If you are working on a project with multiple people, good naming is essential so that you and your teammates can understand the code quickly. If you have to edit or read a piece of code you wrote a few weeks or months later, good module, class, method, and variable names will help you recall what you meant.

67

Chapter 4 Basics of Clean C++

Note  Any entity in a source code base, e.g., files, modules, namespaces, classes, templates, functions, arguments, variables, constants, type aliases, etc., should have meaningful and expressive names.

When I’m designing software or write code, I spend a lot of time thinking about names. I am convinced that it is well-invested time to think about good names, even if it’s sometimes not easy and takes five minutes or longer. I seldom find the perfect name for a thing immediately. Therefore, I rename often, which is easy with a good editor or an Integrated Development Environment (IDE) with refactoring capabilities.

If finding a proper name for a variable, function, or class seems to be difficult or nearly impossible, that might indicate that something else is wrong. Perhaps a design issue exists and you should find and solve the root cause of your naming problem.

The next section includes a few bits of advice for finding good names.

Names Should Be Self-Explanatory

I’ve committed myself to the concept of self-explanatory code. Self-explanatory code is code when no comments are required to explain its purpose (see the following section on comments and how to avoid them). Self-explanatory code requires self-explanatory names for its namespaces, modules, classes, variables, constants, and functions. See Listing 4-2.

Tip  Use simple but descriptive and self-explaining names.

Listing 4-2.  Some Examples of Bad Names

unsigned int num; bool flag;

std::vector<Customer> list; Product data;

Variable naming conventions can often turn into a religious war, but I am very sure that there is broad agreement that num, flag, list, and data are really bad names. What is data? Everything is data. This name has absolutely no semantics. It’s as if you boxed

68

Chapter 4 Basics of Clean C++

your goods into moving boxes and, instead of writing on them what they really contain, for example, “cookware,” you wrote write the word “things” on every single carton. When the cartons arrive at the new house, this information is completely useless.

Listing 4-3 shows an example of how we could better name the four variables in the previous code example.

Listing 4-3.  Some Examples of Good Names

unsigned int numberOfArticles; bool isChanged; std::vector<Customer> customers; Product orderedProduct;

One can now argue that names are better the longer they are. Consider the example in Listing 4-4.

Listing 4-4.  A Very Exhaustive Variable Name

unsigned int totalNumberOfCustomerEntriesWithIncompleteAddressInformation;

No doubt, this name is extremely expressive. Even without knowing where this code comes from, the reader knows quite well what this variable is used for. However, there are problems with names like this. For example, you cannot easily remember such long names. And they are difficult to type if you don’t use an IDE that has auto completion. If such extremely verbose names are used in expressions, the readability of the code may even suffer, as shown in Listing 4-5.

Listing 4-5.  Naming Chaos, Caused By Too Verbose Names

totalNumberOfCustomerEntriesWithIncompleteAddressInformation = amountOfCustomerEntriesWithIncompleteOrMissingZipCode + amountOfCustomerEntriesWithoutCityInformation + amountOfCustomerEntriesWithoutStreetInformation;

Too long and verbose names are not appropriate or desirable when trying to make our code clean. If the context is clear in which a variable is used, shorter and less descriptive names are possible. If the variable is a member (attribute) of a class, for instance, the class’s name usually provides sufficient context for the variable. See Listing 4-6.

69