Expressive Code: People Matter
Download
Report
Transcript Expressive Code: People Matter
Expressive Code:
Micro Design
Greg Vaughn
Sr. Software Technical Specialist
Landsafe, Inc.
[email protected]
Code for People
“Programs must be written for people to
read, and only incidentally for
machines to execute.” – Abelman and
Sussman Structure and Interpretation of Computer
Programs
“Any fool can write code that a computer
can understand. Good programmers
write code that humans can
understand. ” – Martin Fowler
2
Expressive Code
7/20/2015
Monkeys on Your Back
Robust
Requirements
Schedule
Easy to Read,
Easy to Change
Performance
3
Expressive Code
7/20/2015
Monkeys on Your Back
4
Expressive Code
7/20/2015
Why Most Code is Hard to Read
Tactical vs. Strategic Forces
Meet the Schedule…NOW!
Match the Requirements…NOW!
Make it Robust…Soon
Make it Perform…Soon
Make it Easily Maintainable…eh, who cares?
5
Expressive Code
7/20/2015
Why Should I Care?
Code is easier to write than read
Audience is really developers
Not the Computer
In a few months, you’ll be a stranger to your own
code too
Object Oriented development is for people
not for computers
Computer would prefer binary
45-70% of total software cost is in
maintenance state
6
Expressive Code
7/20/2015
Expressive Design
Definitions
API = Application PROGRAMMER
Interface
Hacking = communicating with computer
Designing = communicating with
programmer
Expressive Code is a part of good
design
At the micro level
7
Expressive Code
7/20/2015
Roadmap
Naming
Formatting
Commenting
Idioms
Guiding
Principles
8
Expressive Code
7/20/2015
Naming
The name of a method is an abstraction,
the body is an implementation
9
Expressive Code
7/20/2015
Use of Names
How we typically lookup APIs:
1. Look for appropriately named
package and class
2. Look for appropriately named
method
3. Then – maybe – read detailed
comments
IDE code completion
10
Expressive Code
7/20/2015
Naming Goals
Clear variable and class names
Smaller, well-named methods
Smaller context helps to make
names clearer
IDE code completion
Integer.getInteger() is bad example
11
Expressive Code
7/20/2015
Worst Possible hashCode Method
private int id;
public String getID() {
return “” + id;
}
public int hashCode() {
Integer hashInteger = Integer.getInteger(getID());
if(hashInteger == null) return 0;
else return hashInteger.intValue();
}
Method name is misleading
Integer.getInteger(String s) mistaken for Integer.parseInt(String s)
Actually looks for System.getProperty(s) and converts result to
an Integer
Should have just returned the id
12
Expressive Code
7/20/2015
Java Naming Conventions
Classes capitalized
Constants all caps
Variables and methods begin with
lower-case
Personal: avoid _ or m_, etc. for
instance variables
Compensation for classes and methods
that are too long
13
Expressive Code
7/20/2015
Expected Names
Loop counters: i, j, k
Enumerator: enum
Iterator: iter
Exception: e
Event: evt
Context should be small and clear enough
that longer names add no value
Expressive code is not about being verbose
14
Expressive Code
7/20/2015
Choosing Names
Don’t be afraid of a thesaurus
Use common metaphor or naming
system
Use programming vocabulary
Use business vocabulary
15
Expressive Code
7/20/2015
Rules of Thumb
Ottinger’s Rules for Variable and Class Naming:
http://www.objectmentor.com/publications/naming.htm
Use pronounceable names
Phone test
Avoid encodings
Clarity (don’t be too cute)
One concept, one word
Bidirectional
16
Expressive Code
7/20/2015
Parts of Speech
Classes (and some interfaces) are
Nouns or Noun phrases
Subclasses modify superclasses
Adjectives
Some interfaces modify implementors
Adjectives
Methods act
Verbs (Commands or Requests)
17
Expressive Code
7/20/2015
Naming Don’ts
Don’t rely on intuition
Use glossary if you have unique
concepts
Don’t mislead
Capital ‘O’ lower-case ‘l’
Don’t add artificial context
Every class of Joe’s Cool App begins
JCA
18
Expressive Code
7/20/2015
Useless Name Fragments
Info
Data
Class
Object
Amount
Variable
Type name
addressString, salaryFloat
addAddress(Address addr)
19
Expressive Code
7/20/2015
Formatting
20
Expressive Code
7/20/2015
Brace Placement and Indentation
Religious war
But with technical solution
VCS’s can call custom scripts
Use pretty printer pre-checkin
Developers can then format to their
own preference on checkout
Learn your IDE/editor settings
Avoids formatting noise in diffs
Everybody’s happy!
21
Expressive Code
7/20/2015
Reading Code
We typically scan the left edge
So put important stuff there
Method declarations
Conditionals
Loops
Method Calls
Assignments
22
Expressive Code
7/20/2015
Indentation
For quickly scanning code
Learn IDE/editor settings
Expand tabs to 4 spaces
With 4 space indent (Sun’s coding
standard):
Pay a penalty for getting nested too deeply
Separate loop or conditional bodies in
other methods
23
Expressive Code
7/20/2015
Whitespace
Use it to separate “paragraphs”
Variables that will be used in following
loop
Comments to explain following line or
block
Consider whether it should be a
separate method
24
Expressive Code
7/20/2015
Commenting
/* increment i */
25
Expressive Code
7/20/2015
Target Coding Projects
Typical “bread and butter” business
software
Not
Not
Not
Not
original CS algorithm study
heavy mathematical simulation
medical devices
space shuttle control systems
Failure modes of software
Loss of life
Financial catastrophe
Financial discomfort
26
Expressive Code
7/20/2015
Avoid Comments
Completely self-documenting code is a Platonic
ideal
Probably not achievable
But worth pursuing
Wrong (out of date) comments are worse than no
comments at all
If you feel the need to add a comment, consider:
Rename method
Extract method
Introduce predicate method (isValidFoo())
Use explaining variable
27
Expressive Code
7/20/2015
Top Down Commenting
authenticate() {
/* Find out which authentication method to use */
/* If it's a network connection, authenticate host */
/* Prompt user for password */
/* Validate supplied password */
/* If it doesn't match, raise the alarm */
}
In code:
authenticate() {
findAuthenticationMethod();
if(isNetworkConnection()) authenticateHost();
String password = getPasswordFromUser();
if(!validPassword(password)) {
raiseAlarm();
}
}
28
Expressive Code
7/20/2015
When You Need Comments
Not What
Why
What else you tried
Why it didn’t work
Assumptions
Limitations (to-do)
Reference to algorithm or pattern
Explain API “gotchas”
29
Expressive Code
7/20/2015
Canonical Lousy Comment
for(int i=0; i < elements.length; i++) {
process(elements[i]);
if(elements[i] == 0) {
i++; /*increment i*/
}
}
What?
Comment to code ratio could be a design
goal required by Professor Clueless
30
Expressive Code
7/20/2015
Slightly Better Lousy Comment
for(int i=0; i < elements.length; i++) {
process(elements[i]);
if(elements[i] == 0) {
i++; /*ignore next element */
}
}
Better … but still “what”
31
Expressive Code
7/20/2015
Decent Comment
for(int i=0; i < elements.length; i++) {
process(elements[i]);
if(elements[i] == END_SECTION_MARKER) {
i++; /*padding element for future use*/
}
}
Explains “why” in both code and comment
32
Expressive Code
7/20/2015
Cross Reference Comments
/* this is used by Parser.readFile() */
Tool compensation
Can get easily out of date and WRONG
It’s hard enough to just keep
comments in sync with adjacent code
– this is hopeless
Just say no
33
Expressive Code
7/20/2015
Comments vs. Tests
Let’s get extreme
Unit Tests capture and explain code
So do comments
Tests tell you when they get out of sync
with the code
Comments are easier to read
JUnit
34
Expressive Code
7/20/2015
Idioms
35
Expressive Code
7/20/2015
Different Meaning to Developers
i++
Implies increment by 1 – always
i += x
Add a number to i
Might be 1 this time, but might not next
time
i = x + y
Implies old value of i is unimportant
36
Expressive Code
7/20/2015
Returning Booleans
Don’t do:
if (boolean) {
return true;
} else {
return false;
}
37
Do:
return boolean;
Expressive Code
7/20/2015
Clean up spaghetti conditionals
DeMorgan’s Laws
!(a && b) = (!a) || (!b)
!(a || b) = (!a) && (!b)
Other useful identities
!(x > y) = (x <= y)
!(x >= y) = (x < y)
Explaining variables
Short circuiting
38
Expressive Code
7/20/2015
Example Credit Application
if(!((score > 700) || ((income >= 40000) &&
!(income > 100000) && authorized &&
(score > 500)) || (income > 100000)) {
reject();
} else {
accept();
}
Removing some syntactic noise:
if(!(A || (B && !C && D && E) || F)) {
reject();
} else {
accept();
}
39
Expressive Code
7/20/2015
Example Credit Application
boolean lowRisk = (score > 700) || (income > 100000);
boolean marginalRisk = (score > 500) &&
(40000 <= income) && (income <= 100000);
if(lowRisk || (marginalRisk && authorized)) {
accept();
} else {
reject();
}
Introduced explaining variables
lowRisk was A || F
marginalRisk was B && !C && D
Reversed if/else clauses and removed ‘!’
40
Expressive Code
7/20/2015
Predicate Methods
Complex boolean logic in separate
methods
Well named
isAcceptableRisk()
isUserValid()
canTransitionTo(State s)
41
Expressive Code
7/20/2015
Anti-Example Predicate Method
public static final Boolean isNotRMIModuleEnabled() {
return new Boolean(
!!! isRMIModuleEnabled().booleanValue());
}
!! = nothing
Boolean methods should be
expressed in positive
Don’t call new Boolean(true)
Instead use Boolean.TRUE
Apply these rules and the method
disappears!
42
Expressive Code
7/20/2015
Ternary Operator
if(arr.length == 1) {
label.setText(arr.length + “ result found”);
} else {
label.setText(arr.length + “ results found”);
}
Vs.
label.setText(arr.length + ((arr.length == 1) ?
“ result” : “ results”) + “ found”);
Left edge scan looks for important parts
label.setText(…
if(arr.length…
43
Expressive Code
7/20/2015
Default Switch Statement
Even if your cases cover all possible values,
add a default block so other developers don’t
waste time wondering
Throw IllegalArgumentException if it is
impossible to reach
Later edits won’t leave a hole in the possible
values
Silent failure
44
Expressive Code
7/20/2015
Single Statement Ifs
If you’re going to avoid brackets, place the
statement at the end of the same line as the
if
if(specialCase) x += 2;
Later edits won’t accidentally place a print
statement ahead of the body of the if
if(specialCase)
System.out.println(“special case!”);
x += 2;
Whoops!
45
Expressive Code
7/20/2015
Multiple Exit Points
Don’t fear them
Use if they make things clearer
Better than a bunch of boolean flags
Small methods alleviate concerns
Good for guard clauses
Predicate Methods
46
Expressive Code
7/20/2015
Iterator Idioms
for(Iterator iter = list.iterator(); iter.hasNext();) {
…
Instead of
Iterator iter = list.iterator();
while(iter.hasNext()) {
…
while(true)
Instead of
for(;;)
47
Expressive Code
7/20/2015
Nesting Idiom
DO
if(z.equals(a)) {
…
} else if(z.equals(b)) {
…
} else if(z.equals(c )) {
…
} else {
…
}
DON’T
This is effectively a
switch, not
subordinate if’s
48
if(z.equals(a)) {
…
} else {
if(z.equals(b)) {
…
} else {
if(z.equals(c )) {
…
} else {
…
}
}
}
Expressive Code
7/20/2015
Error Handling Code
If complex, place in separate method
Success flows should dominate
But sometimes it is clearer to detect
errors and abort first
Guard clauses
If you can’t do anything about an
exception
Declare it in your throws clause
Or wrap it in a layered Exception
49
Expressive Code
7/20/2015
Warning Signs
Excessive Parameter List
Hard for client programmer to keep them
all straight
Probably means a new object is hiding in
there
Have you discovered a new abstraction?
Overuse of instanceof
Replace Reflection with Polymorphism
Should you make a new subclass or
interface?
50
Expressive Code
7/20/2015
Guiding Principles
51
Expressive Code
7/20/2015
Redundancy (and Repetition)
DRY Principle (Don’t Repeat Yourself)
“Every piece of knowledge must have a single,
unambiguous, authoritative representation
within a system.” Dave Thomas, Andy Hunt,
Pragmatic Programmer
Once and Only Once
Bug fixes won’t have to happen in multiple
places due to “copy and paste reuse”
Long methods increase the chance of
redundancy
52
Expressive Code
7/20/2015
Goals
Build classes with language primitives
loops, threading constructs, data types, etc.
Build applications with classes
Create higher abstraction “language” of classes
Read code!
Think about why a particular piece is hard or
easy to read
4 am test
Would it be clear to you at 4 am?
53
Expressive Code
7/20/2015
Methods for Clarity
Typical thought:
Create methods for reuse
Instead:
Methods label/explain what block of code
does
Reuse is just a handy side effect
54
Expressive Code
7/20/2015
Writing Methods
Save your short term memory
Write sub-methods with names of each
step you need to do
Becomes your to-do list
Helps to keep them short, and well
named
New IDEs will offer to stub those
methods for you – less typing!
55
Expressive Code
7/20/2015
Inefficiencies
Profile later
Usually you pay for performance with clarity
But not always – new String(“…”)
Hotspot optimizations
Aggressive inlining, etc.
Moore’s law is in your favor
Sometimes it makes more sense to spend money
on hardware
Good developers are expensive
Opportunity costs of new features developers
aren’t working on
56
Expressive Code
7/20/2015
Simplicity
Most software is more complex than it
needs to be
Most software is more complex than
other human endeavors
Break rules if it makes things clearer
57
Expressive Code
7/20/2015
Law of Demeter
“Don’t talk to strangers”.
Within a method, methods can only be
called on the following objects
1. A parameter of the method, (including this)
a. For pragmatic reasons: static methods of another
class
2. An immediate part object
a. An object returned by calling another method of this
b. An attribute of this
c. An element of a collection which is an attribute of this
3. An object created within the method
Apply judiciously!
58
Expressive Code
7/20/2015
Minimize Scope
Declare locals just before they are
needed
We’re not writing C code
Should almost always have initializer
for(Iterator …) vs. while(iter)
Keep methods and classes small and
focused
Don’t expose more than needed
59
Expressive Code
7/20/2015
Closing Thoughts
60
Expressive Code
7/20/2015
Maintaining a Mess
Refactor first
Rename Method is one of easiest
refactorings to automate
Extract Method
Appearing in more and more IDEs
Use JRefactory standalone
You’ll have a better chance once the
existing code is clear
61
Expressive Code
7/20/2015
Obfuscated Code Example
class Div{static $1 $_;class $1{void _$(String $_){System.//
out.print($_);}$1(){_();}void _(){int _,$,_$,$$,__,ä=(1<<5),
å=100,ö=12,åö=ä*ö;å-=1<<4;while(åö>0){for($$=_$=_=__=$=(int)
å;_$>($$-(1<<2));_$(""+(char)(_$)),_$-=1<<1)for(_=$=__-9;_>(
$-6);_-=1<<1,_$(""+(char)(_$!=å?_:ä)));char S$=(char)(å+ö+1)
;_$("te"+S$+(char)(ö+(int)S$));åö--;}}}Div(){$_=new $1();}
public static void main(String []$){Div å=new Div();}}
Or
class Div {
public static void main(String[] args) {
for(int i = 0; i < 100; i++) {
System.out.print(“TIGERteam “);
}
}
}
62
Expressive Code
7/20/2015
Effort – Clarity Curve
Most code fits between those extremes
Good News?
Not Really
Effort
That’s the lazy solution
Takes more work to make it clear
And even more to truly obfuscate it
Clarity
63
Expressive Code
7/20/2015
It’s Finally Working!
Natural reaction to put it away and move on
Even if you don’t fully understand why it works
Coding for machine, not for developers
Terrible expression of intent
Refactor
If it breaks, better you, now, than maintenance
programmer, later
Might be you!
Learn something
Explain “why” in comments
64
Expressive Code
7/20/2015
Stick it to the Maintenance Guy
http://www.dfan.org/writing/comment.html
There was this one hairy part in particular [for
Ultima Underworld AI code] …it had lots of
special cases and weird logic, and after I had
written it I couldn't put it behind me fast enough.
Six months later, I realize that I'm going to have to
muck with this code. "Man," I say to myself, "I
sure hope that I commented this code well when
I originally wrote it." So, with trepidation, I open
up the file and look at the function in question,
and there is exactly one comment in it, at the
very top, and it says:
/* HA HA HA */
65
Expressive Code
7/20/2015
Expressive Code:
Micro Design
Question and Answer
Greg Vaughn
Sr. Software Technical Specialist
Landsafe, Inc.
[email protected]