KEY IDEA
Make all your conditionals, loops, and other changes to control flow as “natural” as possible
1. The Order of Arguments in Conditionals
if (length > 10) Vs if (10 < length)
Left-hand side |
Right-hand side |
---|---|
The expression “being interrogated,” whose value is more in flux. |
The expression being compared against, whose value is more constant. |
2. The Order of if/else Blocks
if (a == b) {
// case one
} else {
// case two
}
if (a != b) {
// case two
} else {
// case one
}
if (!url.HasQueryParameter("expand_all")) { response.Render(items); ... } else { for (int i = 0; i < items.size(); i++) { items[i].Expand(); } }
negative case is the simpler and more interesting/dangerous one
if (not file) {
Log the error
} else {
// ...
}
3. Avoid do/while Loops
do {
if(node.name().equals(name)) return true; node = node.next(); } while (node !== null && --max_length > 0)
while (node != null && max_length-- > 0) {
if (node.name().equals(name)) return true;
node = node.next();
}
Bjarne Stroustrup, the creator of C++, says it best
In my experience, the do-statement is a source of errors and confusion. ... I prefer the condition
“up front where I can see it.” Consequently, I tend to avoid do-statements.
4. Returning Early from a Function
function foo(str) {
if (typeof str !== 'string') return false;
body statment...
}
4. Minimize Nesting
Each level of nesting pushes an extra condition onto the reader’s “mental stack.”
if (user_result === SUCCESS) {
if (permission_result != SUCCESS) { reply.WriteErrors("error permission"); reply.Done(); return; } reply.WriteErrors(""); } else { reply.WriteErrors(user_result); } reply.Done();
Look at your code from a fresh perspective when you’re making changes. Step back and look at it as a whole.
Removing Nesting by Returning Early
if (user_result != SUCCESS) { reply.WriteErrors(user_result); reply.Done(); return; } if (permission_result != SUCCESS) { reply.WriteErrors(permission_result); reply.Done(); return; }
reply.WriteErrors(""); reply.Done();
but sometimes it doesn't work...like below
for (var i = 0; i < results.size(); i++) {
if (results[i] !== NULL) {
non_null_count++;
if (results[i].name !== "") {
console.log("Considering candidate...");
...
}
}
}
Solutions
for (var i = 0; i < results.size(); i++) {
if (results[i] === NULL) continue;
non_null_count++;
if (results[i].name !== "") continue;
console.log("Considering candidate...");
...
}
Breaking Down Giant Expressions
KEY IDEA
Break down your giant expressions into more digestible pieces.
1.Explaining Variables
if (line.split(':')[0].strip() == 'root') { ...
}
var username = line.split(':')[0].strip();
if (username == 'root') { ... }
2.Summary Variables
if (request.user.id === document.owner_id) { // can edit the doc } ...
if (request.user.id !== document.owner_id) { // can't ... }
var canEditable = request.user.id === document.owner_id;
if (canEditable) { ... }
... if (!canEditable) { ... }
3.Morgan's Laws
1) not (a or b or c) ⇔ (not a) and (not b) and (not c)
2) not (a and b and c) ⇔ (not a) or (not b) or (not c)
if (!(file_exists && !is_protected)) Error("Sorry, could not read file.");
// after change
if (!file_exists || is_protected) Error("Sorry, could not read file.");
4.Abusing short-circuit logic
assert((!(bucket = FindBucket(key))) || !bucket.IsOccupied());
var bucket = FindBucket(key);
if (bucket !== null) Assert(!bucket.IsOccpied());
struct Range {
int begin;
int end;
// For example, [0, 5) overlaps with [3, 8)
bool OverlapsWith(Range other);
}
bool OverlapsWith(Range other) {
// Check if 'begin' or 'end' falls inside 'other'
return (begin >= other.begin && begin <= other.end) ||
(end >= other.begin && end <= other.end) ||
(begin <= other.begin && end >= other.end);
}
A more Elegant Approach!
bool OverlapsWith(Range other) {
if (other.end <= begin) return false; // It end before we begin
if (other.begin >= end) return false; // It begin after we end
return true; // Only possibility left: they overlap
}
5.Breaking Down Giant Statements
var updateHighlight = function (msg_num) {
if ($("#vote_value" + msg_num).html() === "UP") {
$("#thumbs_up" + message_num).addClass("highlighted");
$("#thumbs_down" + message_num).removeClass("highlighted");
} else if ($("#vote_value" + msg_num).html() === "DOWN") {
$("#thumbs_up" + message_num).removeClass("highlighted");
$("#thumbs_down" + message_num).addClass("highlighted");
} else {
$("#thumbs_up" + message_num).removeClass("highlighted");
$("#thumbs_down" + message_num).removeClass("highlighted");
}
}
Don't Repeat Yourself
var updateHighlight = function (msg_num) {
var thumbs_up = $("#thumbs_up" + message_num);
var thumbs_down = $("#thumbs_down" + message_num);
var vote_value = $("#vote_value" + msg_num).html();
var hi = "highlighted"
if (vote_value === "UP") {
thumbs_up.addClass(hi);
thumbs_down.removeClass(hi);
} else if (vote_value === "DOWN") {
thumbs_up.removeClass(hi);
thumbs_down.addClass(hi);
} else {
thumbs_up.removeClass(hi);
thumbs_down.removeClass(hi);
}
}
var now = datetime.now();
root_msg.last_view_time = now;
var root_msg.last_view_time = datetime.now();
var remove_one = function (array, value_to_remove) {
var index_to_remove = null; for (var i = 0; i < array.length; i += 1) { if (array[i] === value_to_remove) {
index_to_remove = i; break; }
} if (index_to_remove !== null) {
array.splice(index_to_remove, 1); }
};
var remove_one = function (array, value_to_remove) {
for (var i = 0; i < array.length; i += 1) { if (array[i] === value_to_remove) {
array.splice(i, 1); return; }
}
};
var done = false; while (/* condition */ && !done) { ... if (...) { done = true; continue;
} }
Variables like ‘done’ are
what we call “control flow variables.”
Their sole purpose is
to steer the program’s execution
while (/* condition */) { ... if (...) { break;
} }
KEY IDEA
Make your variable visible by as few lines of code as possible.
class LargeClass {
string str_; void Method1() {
str_ = ...; Method2();
} void Method2() {
// Uses str_ } // Lots methods that don't use str_ ...
};
class LargeClass {
void Method1() {
str_ = ...; Method2(str_);
} void Method2() {
// Uses str_ } // Now other methods can't see str_
};
submitted = false; // Note: global variable var submit_form = function (form_name) {
if (submitted) { return; // don't double-submit the form } ... submitted = true;
};
var submit_form = (function () {
submitted = false;
return function (form_name) {
if (submitted) { return; } ... submitted = true;
}
}());
function test () {
var test_1,
test_2,
test_3,
test_4;
...
useTest_1(test_1);
}
function test () {
var test_2,
test_3,
test_4;
...
var test_1;
useTest_1(test_1);
}