Transcript Chapter 3
Software Engineering Reading
Group: Clean Code
Chapter 3
Led by Nicholas Vaidyanathan
http://www.nicholasvaidyanathan.info
Lead Visionary,Visionary Software Solutions
http://www.visionarysoftwaresolutions.com
Functions matter
Can you understand in 3 minutes?
How about this?
Small!
“The first rule of functions is that they should be
small.”
“The second rule of functions is that they should
be smaller than that.”
“Functions should not be 100 lines long.
Functions should hardly ever be 20 lines long.”
Beck’s Sparkle
◦ “I was used to functions in Swing programs that took
up miles of vertical space. Every function in this
program was just two, or three, or four lines long.
Each was transparently obvious. Each told a story. And
each led you to the next in a compelling order. That’s
how short your functions should be!
3”
Blocks and Indenting
This implies that the blocks within if
statements, else statements, while
statements, and so on should be one line
long. Probably that line should be a
function call.
This also implies that functions should not
be large enough to hold nested
structures.
Therefore, the indent level of a function
should not be greater than one or two.
Do One Thing
FUNCTIONS SHOULD DO ONE THING.
THEY SHOULD DO IT WELL. THEY SHOULD
DO IT ONLY.
If a function does only those steps that are one
level below the stated name of the function, then
the function is doing one thing.
Another way to know that a function is doing
more than “one thing” is if you can extract
another function from it with a name that is not
merely a restatement of its implementation
[G34].
Functions that do one thing cannot be reasonably
divided into sections.
One Level of Abstraction per
Function
In order to make sure our functions are
doing “one thing,” we need to make sure
that the statements within our function are
all at the same level of abstraction.
Mixing levels of abstraction within a function
is always confusing.
◦ Readers may not be able to tell whether a
particular expression is an essential concept or a
detail.
◦ like broken windows, once details are mixed with
essential concepts, more and more details tend to
accrete within the function.
The Stepdown Rule
We want the code to read like a top-down narrative
we want to be able to read the program as though it
were a set of TO paragraphs, each of which is describing
the current level of abstraction and referencing
subsequent TO paragraphs at the next level down.
◦ To include the setups and teardowns, we include setups, then
we include the test page content, and then we include the
teardowns.
◦ To include the setups, we include the suite setup if this is a
suite, then we include the regular setup.
◦ To include the suite setup, we search the parent hierarchy for
the “SuiteSetUp” page and add an include statement with the
path of that page.
◦ To search the parent. . .
Switch Statements
Can be hard to read
Do N things by nature
Poor Payroll
Large, multiple reasons for change
Does more than 1 thing
Violates Single Responsibility Principle
Violates Open-Closed Principle
Opens door for even worse!
◦
◦
isPayday(Employee e, Date date)
deliverPay(Employee e, Money pay)
Bury Switch with OOP
Use Abstract Factory that delegates to
Employee instances through
polymorphism
My general rule for switch statements is
that they can be tolerated if they appear
only once, are used to create
polymorphic objects, and are hidden
behind an inheritance relationship so that
the rest of the system can’t see them
[G23].
Better Payroll
Use Descriptive Names
Remember Ward’s principle: “You know you
are working on clean code when each routine
turns out to be pretty much what you expected.”
Don’t be afraid to make a name long. A long
descriptive name is better than a short
enigmatic name.
◦ A long descriptive name is better than a long
descriptive comment.
Choosing descriptive names will clarify the
design of the module in your mind and help
you to improve it.
Be consistent in your names.
Function Arguments
Ideal Number of arguments for a function is 0
(niladic)
Next comes monadic (1), followed closely by dyadic
(2)
Triadic (3) should be avoided whenever possible
Polyadic (3+) requires justification…and really
shouldn’t be done
Arguments take a lot of conceptual power
◦ They may mix abstractions
Arguments are hard to test
◦ Combinatorial explosion of values for inputs
Output arguments are harder to understand than
input arguments.
Common monadic forms
Common reasons to pass an argument
◦ Ask a question about it
boolean fileExists(“MyFile”)
◦ Transformation and return
InputStream fileOpen(“MyFile”)
transforms a file name String into an InputStream return
value.
◦ Event
void passwordAttemptFailedNtimes(int
attempts)
Use with care. It should be very clear to the reader this is an
event
◦ Try to avoid any monadic functions that don’t
follow these forms, for example
Monadic mistakes
void
includeSetupPageInto(StringB
uffer pageText)
StringBuffer
transform(StringBuffer in) >
void transform(StringBuffer
out)
Flag Arguments
Flag arguments are ugly.
◦
Passing a boolean into a function is a truly
terrible practice.
Complicates the signature of a method,
and loudly proclaims the function does
more than 1 thing!
Dyadic Functions
writeField(name) > writeField(output-Stream,
name)
◦ First is easily parsed, second takes a moment until we
figure out to ignore the first parameter
But we shouldn’t have code that we ignore!
Two arguments make better sense in
makePoint(int x, int Y)
◦ Ordered components of a single value!
Even obvious dyadic functions like
assertEquals(expected, actual) are problematic.
◦ Must memorize the parameter order, since there is
no natural ordering
Triads
Worse than dyads
It’s likely that you should use an Argument
Object
◦ When groups of variables are passed together,
the way x and y are in the example above, they
are likely part of a concept that deserves a
name of its own.
Verbs and Keywords
Choosing good names for a function can
help mitigate parameter ordering issues
In the case of a monad, the function and
argument should form a very nice
verb/noun pair.
◦ write(name) writeField(name)
writeField() is a keyword form of a function name, it
encodes the name of arguments into the function
name
Have no Side Effects
Side effects are LIES
◦ Your function promises to do one thing, but
also does other hidden things
Side effects create temporal couplings
which are confusing and often the source
of bugs
What’s Wrong here?
Output Arguments
appendFooter(s);
◦ Does this function append s as the footer to
something?
◦ Or does it append some footer to s? Is s an input
or an output?
Anything that forces you to check the
function signature is equivalent to a doubletake. It’s a cognitive break and should be
avoided.
much of the need for output arguments
disappears in OO languages because this is
intended to act as an output argument.
Command Query Separation
Functions should either do something or
answer something, but not both.
◦ Either your function should change the state
of an object, or it should return some
information about that object.
◦ Doing both often leads to confusion.
Separate!
public boolean set(String
attribute, String value);
if (set("username", "unclebob"))...
◦ Is it asking whether the “username” attribute
was previously set to “unclebob”?
◦ is it asking whether the “username” attribute
was successfully set to “unclebob”?
Prefer Exceptions to returning
Error Codes
Returning error codes is a subtle violation of
Command Query Separation
◦ Promotes commands being used as predicates in
if statements, leading to deep nesting
Extract try/catch blocks
Error Handling is One Thing
Don’t Repeat Yourself
Duplication is a problem
◦ Requires modification in multiple places on
changes..lots of opportunity for error
Duplication may be the root of all evil in
software.
◦ Many principles and practices have been
created for the purpose of controlling or
eliminating it.
Structured Programming
Edsger Djikstra Rules
◦ Every function and every block within a
function should have one entry and one exit
◦ Only 1 return statement
◦ No break or continue in loops
◦ Never any gotos
How Do You Write Functions
Like This?
Writing software is like any other kind of
writing
◦ When you write a paper or an article,you get
your thoughts down first, then you massage it
until it reads well.
◦ Refactor, Refactor, Refactor!
◦ But write Unit Tests that stress the original
first, and keep them passing!