• No se han encontrado resultados

1253

Classes are currently the best way for programmers to achieve modularity. But

1254

modularity is a big topic, and it extends beyond classes. Over the past several

1255

decades, software development has advanced in large part by increasing the

1256

granularity of the aggregations that we have to work with. The first aggregation

1257

we had was the statement, which at the time seemed like a big step up from

1258

machine instructions. Then came subroutines, and later came classes.

1259

It’s evident that we could better support the goals of abstraction and

1260

encapsulation if we had good tools for aggregating groups of objects. Ada

1261

supported the notion of packages more than a decade ago, and Java supports

1262

packages today.

1263

C++’s and C#’s namespaces are a good step in the right direction, though

1264

creating packages with them is a little bit like writing web pages directly in html.

1265

If you’re programming in a language that doesn’t support packages directly, you

1266

can create your own poor-programmer’s version of a package and enforce it

1267

through programming standards that include

1268

● naming conventions that differentiate which classes are public and which are

1269

for the package’s private use

1270

● naming conventions, code-organization conventions (project structure), or

1271

both that identify which package each class belongs to

1272

● Rules that define which packages are allowed to use which other packages,

1273

including whether the usage can be inheritance, containment, or both

1274

These workaround are good examples of the distinction between programming in 1275

a language vs. programming into a language. For more on this distinction, see 1276

Section 34.4, “Program Into Your Language, Not In It.”

1277

CROSS-REFERENCE This is a checklist of considerations about the quality of the class. For 1278

a list of the steps used to build a class, see the checklist “The Pseudocode Programming

1279

Process” in Chapter 9, page 000.

1280

CHECKLIST: Class Quality

1281

Abstract Data Types

1282

Have you thought of the classes in your program as Abstract Data Types and

1283

evaluated their interfaces from that point of view?

1284

CROSS-REFERENCE For

more on the distinction between classes and packages, see “Levels of Design” in Section 5.2.

Abstraction

1285

Does the class have a central purpose?

1286

Is the class well named, and does its name describe its central purpose?

1287

Does the class’s interface present a consistent abstraction?

1288

Does the class’s interface make obvious how you should use the class?

1289

Is the class’s interface abstract enough that you don’t have to think about

1290

how its services are implemented? Can you treat the class as a black box?

1291

Are the class’s services complete enough that other classes don’t have to

1292

meddle with its internal data?

1293

Has unrelated information been moved out of the class?

1294

Have you thought about subdividing the class into component classes, and

1295

have you subdivided it as much as you can?

1296

Are you preserving the integrity of the class’s interface as you modify the

1297

class?

1298

Encapsulation

1299

Does the class minimize accessibility to its members?

1300

Does the class avoid exposing member data?

1301

Does the class hide its implementation details from other classes as much as

1302

the programming language permits?

1303

Does the class avoid making assumptions about its users, including its

1304

derived classes?

1305

Is the class independent of other classes? Is it loosely coupled?

1306

Inheritance

1307

Is inheritance used only to model “is a” relationships?

1308

Does the class documentation describe the inheritance strategy?

1309

Do derived classes adhere to the Liskov Substitution Principle?

1310

Do derived classes avoid “overriding” non overridable routines?

1311

Are common interfaces, data, and behavior as high as possible in the

1312

inheritance tree?

1313

Are inheritance trees fairly shallow?

1314

Are all data members in the base class private rather than protected?

1315

Other Implementation Issues

1316

Does the class contain about seven data members or fewer?

1317

Does the class minimize direct and indirect routine calls to other classes?

Does the class collaborate with other classes only to the extent absolutely

1319

necessary?

1320

Is all member data initialized in the constructor?

1321

Is the class designed to be used as deep copies rather than shallow copies

1322

unless there’s a measured reason to create shallow copies?

1323

Language-Specific Issues

1324

Have you investigated the language-specific issues for classes in your

1325

specific programming language?

1326 1327

Additional Resources

1328

Classes in General

1329

Meyer, Bertrand. Object-Oriented Software Construction, 2d Ed. New York: 1330

Prentice Hall PTR, 1997. This book contains an in-depth discussion of Abstract

1331

Data Types and explains how they form the basis for classes. Chapters 14-16

1332

discuss inheritance in depth. Meyer provides a strong argument in favor of

1333

multiple inheritance in Chapter 15.

1334

Riel, Arthur J. Object-Oriented Design Heuristics, Reading, Mass.: Addison 1335

Wesley, 1996. This book contains numerous suggestions for improving program

1336

design, mostly at the class level. I avoided the book for several years because it

1337

appeared to be too big (talk about people in glass houses!). However, the body of

1338

the book is only about 200 pages long. Riel’s writing is accessible and enjoyable.

1339

The content is focused and practical.

1340

C++

1341

Meyers, Scott. Effective C++: 50 Specific Ways to Improve Your Programs and 1342

Designs, 2d Ed, Reading, Mass.: Addison Wesley, 1998. 1343

Meyers, Scott, 1996, More Effective C++: 35 New Ways to Improve Your 1344

Programs and Designs, Reading, Mass.: Addison Wesley, 1996. Both of 1345

Meyers’ books are canonical references for C++ programmers. The books are

1346

entertaining and help to instill a language-lawyer’s appreciation for the nuances

1347

of C++.

1348

Java

1349

Bloch, Joshua. Effective Java Programming Language Guide, Boston, Mass.: 1350

Addison Wesley, 2001. Bloch’s book provides much good Java-specific advice

1351

as well as introducing more general, good object-oriented practices.

1352

CC2E.COM/ 0679

CC2E.COM/ 0686

Visual Basic

1353

The following books are good references on classes in Visual Basic:

1354

Foxall, James. Practical Standards for Microsoft Visual Basic .NET, Redmond, 1355

WA: Microsoft Press, 2003.

1356

Cornell, Gary and Jonathan Morrison. Programming VB .NET: A Guide for 1357

Experienced Programmers, Berkeley, Calif.: Apress, 2002. 1358

Barwell, Fred, et al. Professional VB.NET, 2d Ed., Wrox, 2002. 1359

Key Points

1360

● Class interfaces should provide a consistent abstraction. Many problems

1361

arise from violating this single principle.

1362

● A class interface should hide something—a system interface, a design

1363

decision, or an implementation detail.

1364

● Containment is usually preferable to inheritance unless you’re modeling an

1365

“is a” relationship.

1366

● Inheritance is a useful tool, but it adds complexity, which is counter to the

1367

Primary Technical Imperative of minimizing complexity.

1368

● Classes are your primary tool for managing complexity. Give their design as

1369

much attention as needed to accomplish that objective.

1370

7

1

High-Quality Routines

2 Contents 3

7.1 Valid Reasons to Create a Routine

4

7.2 Design at the Routine Level

5

7.3 Good Routine Names

6

7.4 How Long Can a Routine Be?

7

7.5 How to Use Routine Parameters

8

7.6 Special Considerations in the Use of Functions

9

7.7 Macro Routines and Inline Routines

10

Related Topics

11

Steps in routine construction: Section 9.3

12

Characteristics of high-quality classes: Chapter 6

13

General design techniques: Chapter 5

14

Software architecture: Section 3.5

15

CHAPTER 6 DESCRIBED DETAILS of creating classes. This chapter zooms in

16

on routines, on the characteristics that make the difference between a good

17

routine and a bad one. If you’d rather read about high-level design issues before

18

wading into the nitty-gritty details of individual routines, be sure to read Chapter

19

5, “High-Level Design in Construction” first and come back to this chapter later.

20

If you’re more interested in reading about steps to create routines (and classes),

21

Chapter 9, “The Pseudocode Programming Process” might be a better place to

22

start.

23

Before jumping into the details of high-quality routines, it will be useful to nail

24

down two basic terms. What is a “routine?” A routine is an individual method or

25

procedure invocable for a single purpose. Examples include a function in C++, a

26

method in Java, a function or sub procedure in Visual Basic. For some uses,

27

macros in C and C++ can also be thought of as routines. You can apply many of

28

the techniques for creating a high-quality routine to these variants.

29

What is a high-quality routine? That’s a harder question. Perhaps the easiest 30

answer is to show what a high-quality routine is not. Here’s an example of a low-

31

quality routine:

32

C++ Example Of a Low-Quality Routine

33

void HandleStuff( CORP_DATA & inputRec, int crntQtr, EMP_DATA empRec, double

34

& estimRevenue, double ytdRevenue, int screenX, int screenY, COLOR_TYPE &

35

newColor, COLOR_TYPE & prevColor, StatusType & status, int expenseType )

36 { 37 int i; 38 for ( i = 0; i < 100; i++ ) { 39 inputRec.revenue[i] = 0; 40 inputRec.expense[i] = corpExpense[ crntQtr ][ i ]; 41 } 42 UpdateCorpDatabase( empRec ); 43

estimRevenue = ytdRevenue * 4.0 / (double) crntQtr;

44 newColor = prevColor; 45 status = SUCCESS; 46 if ( expenseType == 1 ) { 47 for ( i = 0; i < 12; i++ ) 48

profit[i] = revenue[i] - expense.type1[i];

49

}

50

else if ( expenseType == 2 ) {

51

profit[i] = revenue[i] - expense.type2[i];

52

}

53

else if ( expenseType == 3 )

54

profit[i] = revenue[i] - expense.type3[i];

55

}

56

What’s wrong with this routine? Here’s a hint: You should be able to find at

57

least 10 different problems with it. Once you’ve come up with your own list,

58

look at the list below:

59

● The routine has a bad name. HandleStuff() tells you nothing about what the 60

routine does.

61

● The routine isn’t documented. (The subject of documentation extends

62

beyond the boundaries of individual routines and is discussed in Chapter 19,

63

“Self-Documenting Code.”)

64

● The routine has a bad layout. The physical organization of the code on the

65

page gives few hints about its logical organization. Layout strategies are

66

used haphazardly, with different styles in different parts of the routine.

67

Compare the styles where expenseType == 2 and expenseType == 3. 68

(Layout is discussed in Chapter 18, “Layout and Style.”)

69

● The routine’s input variable, inputRec, is changed. If it’s an input variable, 70

its value should not be modified. If the value of the variable is supposed to

71

be modified, the variable should not be called inputRec. 72

● The routine reads and writes global variables. It reads from corpExpense and 73

writes to profit. It should communicate with other routines more directly 74

than by reading and writing global variables.

75

● The routine doesn’t have a single purpose. It initializes some variables,

76

writes to a database, does some calculations—none of which seem to be

77

related to each other in any way. A routine should have a single, clearly

78

defined purpose.

79

● The routine doesn’t defend itself against bad data. If crntQtr equals 0, then 80

the expression ytdRevenue * 4.0 / (double) crntQtr causes a divide-by-zero 81

error.

82

● The routine uses several magic numbers: 100, 4.0,12, 2, and 3. Magic 83

numbers are discussed in Section 11.1, “Numbers in General.”

84

● The routine uses only two fields of the CORP_DATA type of parameter. If 85

only two fields are used, the specific fields rather than the whole structured

86

variable should probably be passed in.

87

● Some of the routine’s parameters are unused. screenX and screenY are not 88

referenced within the routine.

89

● One of the routine’s parameters is mislabeled. prevColor is labeled as a 90

reference parameter (&) even though it isn’t assigned a value within the 91

routine.

92

● The routine has too many parameters. The upper limit for an understandable

93

number of parameters is about 7. This routine has 11. The parameters are

94

laid out in such an unreadable way that most people wouldn’t try to examine

95

them closely or even count them.

96

● The routine’s parameters are poorly ordered and are not documented.

97

(Parameter ordering is discussed in this chapter. Documentation is discussed

98

in Chapter 20.)

99

Aside from the computer itself, the routine is the single greatest invention in

100

computer science. The routine makes programs easier to read and easier to

101

understand than any other feature of any programming language. It’s a crime to

102

abuse this senior statesman of computer science with code like that shown in the

103

example above.

104

The routine is also the greatest technique ever invented for saving space and

105

improving performance. Imagine how much larger your code would be if you

106

had to repeat the code for every call to a routine instead of branching to the

107

CROSS-REFERENCE The

class is also a good contender for the single greatest invention in computer science. For details on how to use classes effectively, See Chapter 6, “Working Classes.”

routine. Imagine how hard it would be to make performance improvements in

108

the same code used in a dozen places instead of making them all in one routine.

109

The routine makes modern programming possible.

110

“OK,” you say, “I already know that routines are great, and I program with them

111

all the time. This discussion seems kind of remedial, so what do you want me to

112

do about it?”

113

I want you to understand that there are many valid reasons to create a routine and

114

that there are right ways and wrong ways to go about it. As an undergraduate

115

computer-science student, I thought that the main reason to create a routine was

116

to avoid duplicate code. The introductory textbook I used said that routines were

117

good because the avoidance of duplication made a program easier to develop,

118

debug, document, and maintain. Period. Aside from syntactic details about how

119

to use parameters and local variables, that was the total extent of the textbook’s

120

description of the theory and practice of routines. It was not a good or complete

121

explanation. The following sections contain a much better explanation.

122