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

 

Chapter 4 Basics of Clean C++

 

•\ @post for the postconditions of an entity.

 

•\ @throws to document the exception object an entity can throw

 

and the reasons for the exception.

•\

I would not use the @example tag to provide a comment block

 

containing a source code example about how to use an API. As

 

mentioned, such comments add a lot of noise to the code. Instead, I

 

would offer a suite of well-crafted unit tests (see Chapter 2 about unit

 

tests and Chapter 8 about test-driven development), as these are the

 

best examples of use—executable examples! In addition, unit tests

 

are always correct and up to date, as they must be adjusted when the

 

API changes (otherwise the tests will fail). A comment with a usage

 

example, on the other hand, can become wrong without anyone

 

noticing it.

•\

Once a project has been grown to a particular size, it is advisable to

 

pool certain categories of software units with the help of Doxygen’s

 

grouping mechanisms (Tags: @defgroup <name>, @addtogroup

 

<name>, and @ingroup <name>). This is, for example, very useful when

 

you want to express the fact that certain software units belong to a

 

cohesive module on a higher level of abstraction (e.g., a component

 

or subsystem). This mechanism also allows certain categories of

 

classes to be grouped together, for example all entities, all adapters

 

(see the section entitled “Adapter Pattern” in Chapter 9), or all object

 

factories (see the section entitled “Factory Pattern” in Chapter 9).

 

The CustomerAccount class from the previous code example is, for

 

instance, in the group of entities (a group that contains all business

 

objects), but it is also part of the accounting component.

Functions

Functions (methods, procedures, services, operations) are the heart of any software system. They represent the first organizational unit above the lines of code. Well-written functions foster the readability and maintainability of a program considerably. For this reason, they should be well crafted in a careful manner. In this section, I give several important clues for writing good functions.

89

Chapter 4 Basics of Clean C++

However, before I explain the things that I consider to be important for well-crafted functions, let’s examine a deterrent example again, taken from Apache’s OpenOffice 3.4.1. See Listing 4-20.

Listing 4-20.  Another Excerpt from Apache’s OpenOffice 3.4.1 Source Code

1780

sal_Bool BasicFrame::QueryFileName(String& rName, FileType nFileType,

 

sal_Bool bSave )

1781

{

1782

NewFileDialog aDlg( this, bSave ? WinBits( WB_SAVEAS ) :

1783

WinBits( WB_OPEN ) );

1784

aDlg.SetText( String( SttResId( bSave ? IDS_SAVEDLG : IDS_LOADDLG

 

) ) );

1785

 

1786

if ( nFileType & FT_RESULT_FILE )

1787

{

1788

aDlg.SetDefaultExt( String( SttResId( IDS_RESFILE ) ) );

1789

aDlg.AddFilter( String( SttResId( IDS_RESFILTER ) ),

1790

String( SttResId( IDS_RESFILE ) ) );

1791

aDlg.AddFilter( String( SttResId( IDS_TXTFILTER ) ),

1792

String( SttResId( IDS_TXTFILE ) ) );

1793

aDlg.SetCurFilter( SttResId( IDS_RESFILTER ) );

1794

}

1795

 

1796

if ( nFileType & FT_BASIC_SOURCE )

1797

{

1798

aDlg.SetDefaultExt( String( SttResId( IDS_NONAMEFILE ) ) );

1799

aDlg.AddFilter( String( SttResId( IDS_BASFILTER ) ),

1800

String( SttResId( IDS_NONAMEFILE ) ) );

1801

aDlg.AddFilter( String( SttResId( IDS_INCFILTER ) ),

1802

String( SttResId( IDS_INCFILE ) ) );

1803

aDlg.SetCurFilter( SttResId( IDS_BASFILTER ) );

1804

}

1805

 

1806

if ( nFileType & FT_BASIC_LIBRARY )

1807

{

90

 

 

Chapter 4 Basics of Clean C++

1808

 

aDlg.SetDefaultExt( String( SttResId( IDS_LIBFILE f) ) );

1809

 

aDlg.AddFilter( String( SttResId( IDS_LIBFILTER ) ),

1810

 

String( SttResId( IDS_LIBFILE ) ) );

1811

 

aDlg.SetCurFilter( SttResId( IDS_LIBFILTER ) );

1812

 

}

1813

 

 

1814

 

Config aConf(Config::GetConfigName( Config::GetDefDirectory(),

1815

 

CUniString("testtool") ));

1816

 

aConf.SetGroup( "Misc" );

1817

 

ByteString aCurrentProfile = aConf.ReadKey( "CurrentProfile",

 

 

"Path" );

1818

 

aConf.SetGroup( aCurrentProfile );

1819

 

ByteString aFilter( aConf.ReadKey( "LastFilterName") );

1820

 

if ( aFilter.Len() )

1821

 

aDlg.SetCurFilter( String( aFilter, RTL_TEXTENCODING_UTF8 ) );

1822

 

else

1823

 

aDlg.SetCurFilter( String( SttResId( IDS_BASFILTER ) ) );

1824

 

 

1825

 

aDlg.FilterSelect(); // Selects the last used path

1826

// if ( bSave )

1827

 

if ( rName.Len() > 0 )

1828

 

aDlg.SetPath( rName );

1829

 

 

1830

 

if( aDlg.Execute() )

1831

 

{

1832

 

rName = aDlg.GetPath();

1833

/*

rExtension = aDlg.GetCurrentFilter();

1834

 

var i:integer;

1835

 

for ( i = 0 ; i < aDlg.GetFilterCount() ; i++ )

1836

 

if ( rExtension == aDlg.GetFilterName( i ) )

1837

 

rExtension = aDlg.GetFilterType( i );

1838

*/

 

1839

 

return sal_True;

1840

 

} else return sal_False;

1841

}

 

91

Chapter 4 Basics of Clean C++

Question: What did you expect when you saw the member function named

QueryFileName() the first time?

Would you expect that a file selection dialog box is opened (remember the principle of least astonishment discussed in Chapter 3)? Probably not, but that is exactly what is done here. The user is obviously asked to interact with the application, so a better name for this member function would be AskUserForFilename().

But that’s not enough. If you look at the first lines in detail, you will see that there is a Boolean parameter bSave used to distinguish between a file dialog box for opening, and a file dialog box for saving files. Did you expect that? And how does the term Queryf in the function name match that fact? So, a better name for this member function may be AskUserForFilenameToOpenOrSave(). And while looking at this more expressive method name, it should immediately strike you that this method does at least two things and thus violates the single responsibility principle (discussed in detail in Chapter 6).

The following lines deal with the function’s argument nFileType. Apparently, three different file types are distinguished. The nFileType parameter is masked out with something named FT_RESULT_FILE, FT_BASIC_SOURCE, and FT_BASIC_LIBRARY. Depending on the result of this bitwise AND operation, the file dialog box is configured

differently, for example, filters are set. As the Boolean parameter bSave has done before, the three if statements introduce alternative paths. That increases what is known as the cyclomatic complexity of the function.

CYCLOMATIC COMPLEXITY

The quantitative software metric cyclomatic complexity was developed by Thomas J. McCabe, a U.S. mathematician, in 1976.

The metric is a direct count of the number of linearly independent paths through a section of source code, for example, a function. If a function contains no if or switch statement, and no for or while loop, there is just one single path through the function and its cyclomatic complexity is 1. If the function contains one if statement representing a single decision point, there are two paths through the function and the cyclomatic complexity is 2.

If cyclomatic complexity is high, the affected piece of code is typically more difficult to understand, test, and modify, and thus more prone to bugs.

92