Chapter 09 – Part I Creating High-Quality Code
High Quality Routines / Modularity Outline What is a Routine ? Ex. Low Quality Routine Good Routine Name Cohesion and Coupling Routine Length
Routine ? McConnell levels of design Subsystem Modules Routines Definition-”routine is a function, method, or procedure invocable for a single purpose” Examples: C function Java method Ada, Pascal function or procedure 3
C++ Example of low-quality routine void HandleStuff( CORP_DATA & inputRec, int crntQtr, EMP_DATA empRec, double & estimRevenue, double ytdRevenue, int screenX, int screenY, COLOR_TYPE & newColor, COLOR_TYPE & prevColor, StatusType & status, int expenseType ) { int i; for ( i = 0; i < 100; i++ ) { inputRec.revenue[i] = 0; inputRec.expense[i] = corpExpense[crntQtr][i]; }
C++ Example of low-quality routine (conf.) UpdateCorpDatabase( empRec ); estimRevenue = ytdRevenue*4.0/(double)crntQtr; newColor = prevColor; status = SUCCESS; if ( expenseType == 1 ) { for ( i = 0; i < 12; i++ ) profit[i] = revenue[i] - expense.type1[i]; } else if ( expenseType == 2 ) { profit[i] = revenue[i] - expense.type2[i]; else if ( expenseType == 3 ) profit[i] = revenue[i] - expense.type3[i];
Some Problems Routine has a bad name. (tell nothing) and a bad layout Routine isn’t documented Routine reads and writes global variables (reads from corpExpense and writes to profit) Doesn’t have a single purpose Doesn’t defend itself against bad data ( ytdRevenue*4.0/(double)crntQtr causes divide by zero) Have several magic numbers (100,4.0,12,2 and 3) Parameters are unused ( screenX and screenY) Too many parameters
Valid Reasons to create a Routine 1. Reduce complexity (single most important reason) e.g., if ( node <> NULL ) then while ( node.next <> NULL ) do node = node.next leafName = node.name end while else leafName = "" end if Routine like this: leafName = GetLeafName( node ) 7
Valid Reasons to create a Routine (conf.) 2. Avoid duplicate code (the classic reason) Modification easier and more reliable (only need to verify once) e.g., Two routines have the similar code. Solution 1 1. Pull the duplicate code form both routines 2. put a generic version of the common code into base class 3. move two routines into subclass Solution 2 1. Migrate the common code into its own routine 2. let both routines call the new routine Code in one place save space, modification easier 8
Valid Reasons to create a Routine (conf.) 3. Isolate the effect of change ( Improve portability) e.g., HW or OS specific routines 4. Hide Sequences e.g., if you have two lines of code read the top of a stack decrement a stackTop variable Put those two lines of code into a PopStack() routine 5. Hide Pointer Operations e.g., isolating them into routines 9
Others Reasons to create a Routine Hide implementation details Limit effects of changes Make central points of control Facilitate reusable code Accomplish a specific refactoring 10
Design at the Routine Level Good routines have high cohesion and loose coupling Reliability impacted by cohesion 50% fault free with high cohesion 18% fault free with low cohesion Different kinds of good cohesion Functional Sequential Communicational Temporal 11
Good Cohesion Functional Sequential Communicational Temporal Strongest and best kind Module performs one and only one task Sequential Need to be performed in specific order and share data Communicational Make use of the same data (e.g., prints and reinitializes the summary data) Temporal Need to be done at the same time (e.g., Startup()) Should be avoided if possible 12
Unacceptable Cohesion Procedural Done in a specified order, but don’t share data Sequential--Need to be performed in specific order and share data Logical Several operations in same routine and selected via switch Very common in code Most often to share common code Coincidental ( “no cohesion” ) No discernible relationship to each other (e.g., written by same programmer, share common code segments) 13
Coupling Coupling refers to strength of connection between routines Coupling is complement to cohesion Goal is routines with Loose coupling Strong cohesion Loose coupling implies Minimal reliance on other routines Simple as possible interface 14
Coupling Criteria Size--number of parameters single over multi simple over structured Visibility--prominence of connection well advertised, obvious – parameter list sneaky – global data Flexibility--how easy to change connection 15
Levels of Coupling Simple-data (passed through parameter list and is simple data) Data-Structure (passed through parameter list, but data is structured) Control (passed data to tell another routine what to do) Global data (make use of common data Also known as common coupling Pathological (using code between routines) 16
Example 1 What can we say about this routines: Coupling, Cohesion /** computes the max or min of a pair of integers. If isMax is true computes max otherwise returns min */ public int maxOrMin(int a, int b, boolean isMax) { if (isMax) return a < b? b : a; } else return a < b? a: b; 17
Example 2 What can we say about this routines: Coupling, Cohesion /** computes the max or min of a pair of integers. If isMax is true computes max otherwise returns min */ public int maxOrMin2(int a, int b, boolean isMax) { if (a < b) return isMax? b : a; } else return isMax? a: b; 18
Example 3 public class Person{ private Date m_birthDate; private String m_name; public String getName() {return m_name;}; public Date getBirthDay() {return m_birthDate;}; } public class Util { public boolean is21(Person person) { Date birthday=person.getBirthDay(); Date beerTime = new Date(birthday.getYear() + 21, birthday.getMonth(), birthday.getDate()); return birthday.after(beerTime); } 19
Good Routine Names For procedure name in non-Object-oriented languages Strong verb followed by an object (noun) e.g., PrintDocument(), CheckOrderInfo() For object-oriented languages use a strong verb Describe everything the module does (too long many indicate low cohesion) Avoid wishy-washy verbs (e.g., handle, process, perform) Make names as long as necessary (20-35 chars versus 15-20) 20
Good Routine Names (conf.) Use description of the return value (e.g., cos(), customerId.Next(), printer.IsReady()) Use opposites precisely Helps consistency, readability e.g., add/remove, get/set, insert/delete, next/previous Establish conventions (e.g., get, set, increment, initialize) 21
Routine Length Highly controversial topic 1970’s IBM limit 50 lines TRW one to two pages Evidence supports shorter versus longer Many studies found inverse correlation, but… Some studies didn’t find any correlation Certainly not positive correlation Use over 200 lines with caution 22
Check List : Big Picture Issues Is the reason for creating the routine sufficient? Have all parts of the routine that would benefit from being put into routines of their own been put into routines of their own? Is the name a strong, clear verb-object name for a procedure or an object name for a function? Does the name describe everything the routine does? Does it have strong cohesion—doing one and only one thing extremely well? Does it have loose coupling—is the connection to other routines small, intimate, visible, and flexible? Is the length of the routine determined naturally by its function and logic, rather than artificially by a coding standard? 23
Check List : Parameter passing Do the formal and actual parameters match? Are the routine's parameters in a sensible order, including matching the order of similar routines? Are interface assumptions documented? Does the routine have seven or fewer parameters? Are only the parts of structured variables that are needed passed to the routine, rather than the whole variable? Is each input parameter used? Is each output parameter used? If the routine is a function, does it return a value under all possible circumstances? 24
Check List : Defensive programming Are assertions used to document assumptions? Does the routine protect itself from bad input data? Does the routine handle bad data gracefully? Is the routine designed to handle changes gracefully? Have debugging aids been installed in a way that they can be activated or deactivated without a great deal of fuss? Have errors been firewalled so they don't affect code outside the routine? Does the routine check function return values? Is the defensive code that's left in production code designed to help the user rather than the programmer? 25
Summary Valid Reasons to create a routine. Many reasons but the most import is: reduce complexity Coupling (goal is loose) Pass the minimal amount and the simplest kind of data Cohesion Ideally routine does only one thing Examples Poor coupling and cohesion Poor coupling Program Length Shorter is better than longer (maybe) 26