Cover of Code Complete by Steve McConnell - Business and Economics Book

From "Code Complete"

Author: Steve McConnell
Publisher: Pearson Education
Year: 2004
Category: Computers

🎧 Free Preview Complete

You've listened to your free 10-minute preview.
Sign up free to continue listening to the full summary.

🎧 Listen to Summary

Free 10-min Preview
0:00
Speed:
10:00 free remaining
Chapter 5: Code Improvements
Key Insight 3 from this chapter

Specific Refactoring Techniques and Safe Practices

Key Insight

A comprehensive catalog of refactorings provides concrete techniques to address code smells across various levels of software structure. Data-level refactorings include replacing 'magic numbers' like 3.14 with named constants (e.g., PI) and renaming unclear variables, constants, classes, or routines for improved clarity. Expressions can be moved inline for simplicity or replaced by routines to eliminate duplication, while intermediate variables can clarify complex expressions. Converting multiuse variables (e.g., i, j, temp) into multiple single-use variables with specific names, utilizing local variables instead of input-only parameters for local purposes, and transforming data primitives (e.g., Money, Temperature) or sets of type codes (e.g., SCREEN=0) into classes or enumerations enhance type safety and semantic richness; type codes with differing behaviors might become a class hierarchy with subclasses. Arrays with heterogeneous elements should be converted into objects with named fields, and collections should be encapsulated, returning read-only versions with explicit add/remove routines. Traditional records are best replaced by data classes to centralize operations like error checking and persistence. Statement-level refactorings focus on simplifying control flow and expressions, such as decomposing complex boolean expressions with intermediate variables or moving them into dedicated boolean functions, consolidating duplicated code within conditional blocks, and using 'break' or 'return' instead of loop control variables. Routines become clearer by returning as soon as the result is known, avoiding convoluted logic, and leveraging polymorphism to replace repetitive 'case' statements. Using null objects (e.g., representing an unknown resident as 'occupant' within the Customer class itself) centralizes null handling.

Routine-level refactorings encompass extracting inline code into new routines or, conversely, moving simple, self-explanatory routine code inline. Overly long routines can be converted into classes for further modularization and factoring. Substituting complex algorithms with simpler alternatives, adding or removing parameters as needed, and strictly separating query operations from state-modifying operations (Command-Query Separation) enhance routine design. Similar routines can be combined by parameterizing differing values, or routines whose behavior depends on input parameters can be separated into distinct functions. The decision to pass a whole object or specific fields to a routine depends on maintaining the correct interface abstraction, while encapsulating downcasting ensures routines return the most specific object type. Class implementation refactorings involve changing between value and reference objects based on object complexity and housekeeping needs. Replacing virtual routines with data initialization in subclasses, where subclasses only vary by constant values, leverages generic base class code. Adjusting member routine or data placement (pulling up to a superclass to eliminate duplication, pushing down to derived classes for specialization, or pulling up constructor bodies) refines inheritance hierarchies. Specialized code used by only a subset of instances should be extracted into subclasses, and similar code found in multiple subclasses should be combined into a superclass.

Class interface refactorings aim for coherent and robust external contracts, including moving routines to more appropriate classes, splitting classes with multiple responsibilities, or eliminating classes that perform very little. Adjusting delegation relationships, such as hiding delegates or removing middleman objects, depends on preserving interface integrity and promoting direct interaction. The choice between replacing inheritance with delegation (for greater control) or delegation with inheritance (when a delegate's entire public interface is exposed) offers flexibility in class relationships. Functionality for unmodifiable classes can be extended by introducing foreign routines or extension classes. Encapsulating public data members behind access routines, removing Set() routines for immutable fields, hiding routines not intended for external use, and encapsulating unused routines all contribute to stronger encapsulation and clearer interfaces. A superclass and subclass can be collapsed if their implementations are highly similar. System-level refactorings address broader architectural concerns: creating a definitive reference source for data managed externally (e.g., GUI controls), adjusting unidirectional or bidirectional class associations as required, providing factory methods for object creation based on type codes or reference objects, and standardizing error handling via exceptions or error codes for consistency. Crucially, safe refactoring mandates a systematic approach: always save the original code, perform changes in small and singular steps, list planned actions, use a 'parking lot' for mid-refactoring ideas, retest after each refactoring, review complicated or mission-critical changes, assess associated risks, ensure the internal quality of the program is enhanced, and never use refactoring as a disguise for quick fixes or an excuse to avoid a complete rewrite of genuinely poor code.

📚 Continue Your Learning Journey — No Payment Required

Access the complete Code Complete summary with audio narration, key takeaways, and actionable insights from Steve McConnell.