Creating High-Quality
Code
Hoa Nguyen
Outline
Chapter 5: Design in Construction
Chapter 6: Working Classes
Chapter 7: High-Quality Routines
Chapter 8: Defensive Programming
Chapter 9: The Pseudo code Programming Process
Chapter 5: Design in Construction
5.1 Design Challenges
5.2 Key Design Concepts
5.3 Design Building Blocks: Heuristic
5.4 Design Practices
5.5 Comments on Popular Methodologies
5.1 Design Challenges
What is “Software Design” mean?
The conception, invention, or contrivance of a scheme for
turning a specification for computer software into
operational software
The activity that links requirements to coding and debugging
5.1 Design Challenges
Design is a Wicked Problem
Design is a Sloppy Process
Design is about Tradeoffs and Priorities
Design involves Restrictions
Design is Nondeterministic
Design is Heuristic Process
Design is Emergent
5.2 Key Design Concepts
Good design depends on understanding a handful of key
concepts. This section discusses the role of complexity,
desirable characteristics of designs, and levels of design
5.2 Key Design Concepts
Managing Complexity
Accidental and Essential Difficulties
Importance of Managing Complexity
Divide system into sub-systems
Keep the routines short
Write code easy for understand
How to attach Complexity
Minimum the amount of essential complexity that anyone’s
brain has to deal with at any one time
Keep accidental complexity from needlessly proliferating
5.2 Key Design Concepts
Desirable Characteristics of Design
Minimal complexity (Primary goal)
Avoid make “clever” design
Ease of maintenance
Loose coupling
Extensibility
Reusability
High fan-in
5.2 Key Design Concepts
Desirable Characteristics of Design
Low-to-medium fan-out
Portability
Leanness
Stratification
Standard techniques
5.2 Key Design Concepts
Levels of design
Software system
Division into subsystems
or package
5.2 Key Design Concepts
Levels of design
Software system
Division into subsystems or package
Common Subsystems: Business rules, User interface, Database
access, System dependencies,
Division into classes
Division into routines
Internal routine design
5.3 Design Building Blocks: Heuristics
Find real world objects
Identify the objects and their attributes (methods and data)
Determine what can be done to each object
Determine what each object is allowed to do to other objects
Determine the parts of each object that will be visible to other
objects-which parts will be public and which will be private
Define each object’s public interface
5.3 Design Building Blocks: Heuristics
Find real world objects
Determine
Define thewhat
Identifyeachthe each
object’s
objects
parts object
can and
be
public
of done
each is allowed
interface
their to eachthat
object to do be
object
attributes to visible
(methods
will other
and objects
data)
to other
5.3 Design Building Blocks: Heuristics
Form Consistent Abstractions
5.3 Design Building Blocks: Heuristics
Encapsulate Implementation Detail
5.3 Design Building Blocks: Heuristics
Inherit – When Inheritance Simplifies Design
Hide Secrets (Information Hiding)
Secrets and the Right to Privacy
5.3 Design Building Blocks: Heuristics
Inherit – When Inheritance Simplifies Design
Hide Secrets (Information Hiding)
An Example of Information Hiding
Two categories of Secret
5.3 Design Building Blocks: Heuristics
Inherit – When Inheritance Simplifies Design
Hide Secrets (Information Hiding)
Barrier to information hiding
Excessive distribution of information
Circular dependencies
Class data mistaken for global data
Perceived preformation penalties
Value of information hiding
5.3 Design Building Blocks: Heuristics
Identify Areas Likely to Change
Identify items that seem likely to change
Separate items that are likely to change
Isolate items that seem likely to change
5.3 Design Building Blocks: Heuristics
Identify Areas Likely to Change
A few areas that are likely to change
Business rules
Hardware dependencies
Input and Output
Nonstandard language features
Difficult design and construction areas
Status variables
Data-size constraints
5.3 Design Building Blocks: Heuristics
Keep coupling loose
Coupling Criteria
Size
Visibility
Flexibility
Kinds of Coupling
Simple-data-parameter coupling
Simple-object coupling
Object-parameter coupling
Semantic coupling
5.3 Design Building Blocks: Heuristics
Look for Common Design Patterns
Patterns reduce complexity by providing ready-made
abstractions
Patterns reduce error by institutionalizing details of common
solutions
Patterns provide heuristic value by suggesting design
alternatives
Patterns streamline communication by moving the design
dialog to a higher level
5.3 Design Building Blocks: Heuristics
Look for Common Design Patterns
5.3 Design Building Blocks: Heuristics
Other Heuristics
Aim for strong Cohesion
Build Hierarchies
Formalize Class Contracts
Assign Responsibilities
Design for Test
Avoid Failure
5.3 Design Building Blocks: Heuristics
Other Heuristics
Choose Binding Time Consciously
Make Central Points of Control
Consider Using Brute Force
Draw a Diagram
Keep your Design Modular
5.4 Design Practices
Iterate
Divide and Conquer
Top-Down and Bottom-Up Design Approaches
Argument for Top Down
Argument for Bottom Up
Experimental Prototyping
Collaborative Design
How much Design is Enough?
5.4 Design Practices
Capturing your Design Work
Insert design document into the code itself
Capture design discussions and decisions on a Wiki
Write email summaries
Use a digital cameras
Save design flip charts
Use CRC (Class, Responsibility, Collaborator) cards,
Create UML diagrams at appropriate levels of detail
5.5 Comments on Popular Methodologies
Capturing your Design Work
Insert design document into the code itself
Capture design discussions and decisions on a Wiki
Write email summaries
Use a digital cameras
Save design flip charts
Use CRC (Class, Responsibility, Collaborator) cards,
Create UML diagrams at appropriate levels of detail
Chapter 5: Design in Construction
Key Points
Chapter 6: Working Classes
Class is a collection of data and routines that share a
cohesive, well-defined responsibility
A key to being an effective programmer is maximizing the
portion of a program that you can safely ignore while
working on any one section of code
Classes are the primary tool for accomplishing that
objective
Chapter 6: Working Classes
6.1 Class Foundation: Abstract Data Type (ADTs)
6.2 Good Class Interfaces
6.3 Design and Implementation Issues
6.4 Reasons to Create a Class
6.5 Language-Specific Issues
6.6 Beyond Classes: Packages
6.1 Abstract Data Type (ADTs)
A collection of data and operations that work on that data
An ADT might be a graphic window with all the operations
that affect it, a file and file operations, an insurance-rates
tables and the operations on it, …
6.1 Abstract Data Type (ADTs)
ADTs and Classes
ATDs form the foundation for the concept of classes
Class is ADT + Inheritance + Polymorphism
6.1 Abstract Data Type (ADTs)
Benefits
Hide implementation details
Changes do not affect the whole program
Make the interface more informative
Easier to improve performance
Program is more obviously correct
Program becomes more self-documenting
Do not have to pass data all over program
Able to work with real-world entities rather with low level
implementation structures
6.1 Abstract Data Type (ADTs)
Some examples
6.1 Abstract Data Type (ADTs)
Some examples
6.1 Abstract Data Type (ADTs)
Guidelines
Build or use typical low-level data types as ADTs, not at low-
level data types
Treat common objects such as files as ADTs
Treat even simple items as ADTs
Refer to an ADT independently of the medium it’s store on
6.1 Abstract Data Type (ADTs)
Handling Multiple Instances of Data with ADTs in Non-Object-
Oriented Environments
Option 1: Explicitly identify instances each time you use ADT
service
Option 2: Explicitly provide the data used by ADT services
Option 3: Use implicit instances
6.2 Good Class Interfaces
Good Abstraction - Define
Abstraction is the ability to view a complex operation in a simplified
form
6.2 Good Class Interfaces
Good Abstraction – How to get
Present a consistent level of abstraction in the class interface
6.2 Good Class Interfaces
Good Abstraction
Present a consistent level of abstraction in the class interface
Be sure you understand what abstraction the class in
implementing
Provide services in pairs with their opposites
Move unrelated information to another class
Make interfaces programmatic rather than semantic when possible
6.2 Good Class Interfaces
Good Abstraction
Beware of erosion of the interface’s abstraction under
modification
Do not add public members that are inconsistent with the
interface abstraction
Consider abstraction and cohesion together
6.2 Good Class Interfaces
Good Encapsulation
Minimize accessibility of classes and members
Do not expose member data in public
Avoid putting private implementation details into a class’s
interface
Do not make assumptions about class’s users
Avoid friend classes
Do not put a routine into the public interface just because it
uses only public routines
Favor read-time convenience to write-time convenience
Be very, very wary of semantic violations of encapsulation
6.3 Design and Implementation Issues
Containment (“has a” Relationships)
Implement “has a” through containment
Implement “has a” through private inheritance as last resort
Be critical of classes that contain more than about seven data
members
6.3 Design and Implementation Issues
Inheritance (“is a” Relationships)
Implement “is a” through public inheritance
Design and document for inheritance or prohibit it
Adhere to the Liskov Substitution Principle (LSP)
Be sure to inherit only what you want to inherit
6.3 Design and Implementation Issues
Inheritance (“is a” Relationships)
6.3 Design and Implementation Issues
Inheritance (“is a” Relationships)
Do not “override” a non-overridable member function
Move common interfaces, data, and behavior as high as
possible in the inheritance tree
Be suspicious of classes of which there is only one instance
Be suspicious of base classes of which there is only one derived
class
Be suspicious of classes that override a routine and do nothing
inside the derived routine
Avoid deep inheritance trees
6.3 Design and Implementation Issues
Inheritance (“is a” Relationships)
Prefer polymorphism to extensive type checking
Make all data private, not protected
6.3 Design and Implementation Issues
When use “Inheritance” and when use “Containment”
6.3 Design and Implementation Issues
Member Functions and Data
Keep the number of routines in a class as small as possible
Disallow implicitly generated member functions and operations
you do not want
Minimize the number of different routines call by a class
Minimize indirect routine calls to other classed
In general, minimize the extent to which a class collaborates
with other classes
6.3 Design and Implementation Issues
Constructors
Initialize all member data in all constructors if possible
Enforce the singleton property by using a private constructor
Prefer deep copies to shallow copies until proven otherwise
6.4 Reasons to Create a Class
Model real-world objects
Model abstract objects
Reduce complexity
Isolate complexity
Hide implementation details
Limit effect of changes
Hide global data
Streamline parameter passing
Make central points of control
6.4 Reasons to Create a Class
Facilitate reusable code
Plan for a family of programs
Package related operations
Accomplish a specific refactoring
Classes to avoid
Avoid creating god classes
Eliminate irrelevant classes
Avoid classes named after verbs
6.5 Language-Specific Issues
6.6 Beyond Classes: Package
Standards for creating own package
Chapter 6: Working Classes
Key Points
Chapter 7: High-Quality Routines
7.1 Valid Reasons to Create a Routine
7.2 Design at the Routine Level
7.3 Good Routine Names
7.4 How Long Can a Routine Be?
7.5 How to Use Routine Parameters
7.6 Special Considerations in the Use of Functions
7.7 Macro Routines and Inline Routines
7.1 Valid Reasons to Create a Routine
Reduce complexity
Introduce an intermediate, understandable abstraction
7.1 Valid Reasons to Create a Routine
Avoid duplicate code
Support sub-classing
Hide sequences
Hide pointer operations
Improve portability
Simplify complicated Boolean tests
Improve performance
7.1 Valid Reasons to Create a Routine
In addition, many of reason to create a class are also good
reasons to create a routine:
Isolate complexity
Hide implementation details
Limit effects of changes
Hide global data
Make central point of control
Facilitate reusable code
Accomplish a specific refactoring
7.2 Design at the Routine Level
Goal
Each routine do one thing well and not do anything else
How to make routines as cohesive as possible
Functional cohesion
A routine performs one and only one operation
Sequential cohesion
Communicational cohesion
Temporal cohesion
7.2 Design at the Routine Level
Unacceptable kinds of cohesion
Procedural cohesion
Logical cohesion
Coincidental cohesion
7.3 Good Routine Names
Describe everything the routine does
Avoid meaningless, vague, or wishy-washy verbs
Do not differentiate routine names solely by number
Make names of routines as long as necessary
To name a function, use a description of the return value
To name a procedure, use a strong verb followed by an
object
Use opposites precisely
Establish conventions for common operations
7.4 How long can a Routine Be?
Allow to grow up to 100-200 lines (without comments,
blank lines)
7.5 How to Use Routine Parameters
Put parameters in input-modify-output order
Consider creating your own in and out keywords
If several routines use similar parameters, put the similar
parameters in a consistent order
Use all the parameters
Put status or error variables last
Do not use routine parameters as working variables
7.5 How to Use Routine Parameters
Document interface assumptions about parameters
Limit the number of routine’s parameters to about seven
Consider an input, modify, output naming convention for
parameters
Pass the variables or objects that the routine needs to
maintain its interface abstraction
Use Named Parameters
Make sure actual parameters match formal parameters
7.6 Special Considerations in the Use of
Functions
Special Considerations in the Use of Functions
When to Use a Function and When to Use a Procedure
Setting the Function’s Return Value
Check all possible return paths
Do not return references or pointers to local data
7.7 Macro Routines and Inline Routines
How to use Macro Routines and Inline Routines in C++
Chapter 7: High-Quality Routines
Key Points
Chapter 8: Defensive Programing
8.1 Protecting Your Program from Invalid Inputs
8.2 Assertions
8.3 Error-Handling Techniques
8.4 Exceptions
8.5 Barricade Your Program to Contain the Damage Caused
by Errors
8.6 Debugging Aids
8.7 Determining How Much Defensive Programming to Leave
in Production Code
8.8 Being Defensive About Defensive Programing
8.1 Protecting Your Program from Invalid
Inputs
Check the values of all data from external sources
Check the values of all routine input parameters
Decide how to handle bad inputs
8.2 Assertions
Assertion is code that’s used during development – usually a
routine or macro – that allow a program to check itself as it
runs
When an assertion is true, that means everything is
operating as expected. When it’s false, that means it has
detected an unexpected error in the code
An Assertion usually takes 2 parameters: a Boolean
expression that describes the assumption that’s supposed to
be true, and a message to display if it is not.
8.2 Assertions
Guideline for using Assertion
Use error-handling code for conditions you expect to occur;
use assertions for conditions that should never occur
Avoid putting executable code into assertions
Use Assertions to document and verify pre-conditions and post-
conditions
For highly robust code, assert and then handle the error
anyway
8.3 Error Handling Techniques
Return a neutral value
Substitute the next piece of valid data
Return the same answer as the previous time
Substitute the closest legal value
Log a warning message to a file
Return an error code
Call an error-processing routine/object
Display an error message whenever the error is encountered
Handle the error in whatever way works best locally
Shutdown
8.4 Exceptions
Use exception to notify other parts of program about errors
that should not be ignored
Throw an exception only for conditions that are truly
exceptional
Do use an exception to pass the bulk
Avoid throwing exceptions in constructors and destructors
unless you catch them in the same place
Throw exceptions at the right level of abstraction
8.4 Exceptions
Include in the exception message all information that led to
the exception
Avoid empty catch blocks
Know the exceptions your library code throws
Consider building a centralized exception reporter
Standardize your project’s use of exceptions
Consider alternatives to exceptions
8.5 Barricade Your Program to Contain
the Damage Caused by Errors
8.5 Barricade Your Program to Contain
the Damage Caused by Errors
Convert input data to the proper type at input time
8.6 Debugging Aids
Do not automatically apply production constraints to the
development version
Introduce debugging aids early
Plan to remove Debugging Aids
8.7 Determine how much defensive
program to leave in production code
Leave in code that checks for important errors
Remove code that checks for trivial errors
Remove code that results in hard crashes
Leave in code that helps the program crash gracefully
Log errors for your technical support personnel
Make sure that the error messages you leave in are friendly
8.8 Being defensive about defensive
programing
Think about where you need to be defensive, and set your
defensive programing priorities accordingly
Chapter 8: Defensive Programing
Key points
Chapter 9: The Pseudo code Programing
Process
9.1 Summary of steps in Building Classes and Routines
9.2 Pseudo code for Pros
9.3 Constructing Routines for using PPP
9.4 Alternatives to the PPP
9.1 Summary of Steps in Building Classes
and Routines
Steps in Creating a Class
9.1 Summary of Steps in Building Classes
and Routines
Steps in Building a Routine
9.2 Pseudo code for Pros
Guideline for using Pseudo code effectively
9.2 Pseudo code for Pros
Guideline for using Pseudo code effectively
9.2 Pseudo code for Pros
Benefits
Make review design easier
Support idea of iterative refinement
Make change easier
Minimize commenting effort
Easier to maintain than other forms of design document
9.3 Constructing Routines by using the
PPP
Design the Routine
Check the prerequisites
Define the problem the routine will solve
Name of routine
Decide how to test the routine
Research functionality available in the standard libraries
9.3 Constructing Routines by using the
PPP
Design the Routine
Think about error handling
Think about efficiency
Research the algorithms and data types
Write the pseudo code
Think about the data
Check the pseudo code
Try a few ideas in pseudo code, and keep the best (iterate)
9.3 Constructing Routines by using the
PPP
Code the Routine
9.3 Constructing Routines by using the
PPP
Check the code
Mentally check the routine for errors
Compile the routine
Step through the code in the debugger
Test the code
Remove errors from the routine
9.3 Constructing Routines by using the
PPP
Clean up Leftovers
Check the routine’s interface
Check for general design quality
Check the routine’s variables
Check the routine’s statements and logic
Check the routine’s layout
Check the routine’s documentation
Remove redundant comments
9.4 Alternatives to PPP
Alternatives to PPP
Test-first development
Refactoring
Design by contract
Hacking?
Chapter 9: The Pseudo code Programing
Process
Key Points