PREFACE
Chapter 1 Code should be easy to understand
The Fundamental Theorem of Readability
ควรเขียนโค้ดเพื่อลดเวลาที่คนอื่นต้องใช้ให้น้อยที่สุดเพื่อเข้าใจมัน.
Is Smaller Always Better?
โค้ดที่สั้นนั้นดีแต่จะดีขึ้นถ้าลดเวลาเข้าใจมันไปอีก
Does Time-Till-Understanding Conflict with Other Goals?
คุณอาจกำลังคิดว่า แล้วข้อจำกัดอื่นๆ เช่น การทำให้โค้ดมีประสิทธิภาพ หรือการออกแบบที่ดี หรือ
ง่ายต่อการทดสอบและอื่น ๆ ? บางครั้งสิ่งเหล่านี้ขัดแย้งกับการต้องการให้โค้ดเข้าใจง่ายใช่หรือไม่
คำตอบคือการเข้าใจง่ายมักจะนำไปสู่โค้ดที่ออกแบบมาอย่างดีและทดสอบได้ง่าย
SURFACE-LEVEL IMPROVEMENTS
การปรับปรุงชื่อให้ดีขึ้น การเขียน comment ที่ดี การจัดวางรูปแบบโค้ด คุณสามารถทำมันได้โดยไม่เปลี่ยนโครงสร้างโค้ดหรือไม่?
หัวข้อเหล่านี้มีความสำคัญมากเพราะมีผลกับโค้ดทุกบรรทัดใน codebase ของคุณ
แม้ว่าการเปลี่ยนแปลงแต่ละครั้งอาจดูเล็กน้อย แต่โดยรวมแล้วสามารถปรับปรุง a . ได้อย่างมาก
โค้ดเบส หากโค้ดของคุณมีชื่อที่ยอดเยี่ยม ความคิดเห็นที่เขียนอย่างดี และการใช้ช่องว่างที่สะอาดหมดจด
รหัสของคุณจะอ่านง่ายกว่ามาก
Chapter 2 Packing Information into Names
Pack information into your names.
ชื่อจำนวนมากที่เราเห็นในโปรแกรมนั้นคลุมเครือ เช่น tmp แม้แต่คำที่ดูเหมือนเหมาะสมเช่น size or get อย่านำมาใช้
This chapter is organized into six specific topics:
- Choosing specific words
- Avoiding generic names (or knowing when to use them) อย่าใช้คำทั่วไป
- Using concrete names instead of abstract names การใช้ชื่อที่เป็นรูปธรรมแทนชื่อนามธรรม
- Attaching extra information to a name, by using a suffix or prefix
- Deciding how long a name should be
- Using name formatting to pack extra information
ลองหาคำที่มีสีสัน
send -> deliver, dispatch, announce, distribute,route
find -> search, extract, locate, recover
start -> launch, create,begin,open
make -> create, set up, build , generate, compose, add, new
ใน php มี function explode() และ split ยากที่จะเดาด้วยชื่อว่ามันต่างกันอย่างไร
ชัดเจนและแม่นยำดีกว่าน่ารัก
การเรียกคืนชื่อไม่ได้บรรจุข้อมูลมากนัก ให้ใช้ชื่อที่อธิบาย .แทน
ค่าของตัวแปร
ชื่อ tmp ควรใช้เฉพาะในกรณีที่มีอายุสั้นและชั่วคราว
เป็นความจริงที่สำคัญที่สุดเกี่ยวกับตัวแปรนั้น
ชื่อเช่น i, j, iter และมักใช้เป็นดัชนีและตัววนซ้ำ ถึงแม้ว่า
ชื่อเหล่านี้เป็นชื่อสามัญ เข้าใจว่าหมายถึง "ตัววนซ้ำ" (อันที่จริงถ้าคุณใช้
หนึ่งในชื่อเหล่านี้เพื่อจุดประสงค์อื่น อาจทำให้สับสนได้ ดังนั้นอย่าทำเช่นนั้น!)
The Verdict on Generic Names
หากคุณกำลังจะใช้ชื่อทั่วไปเช่น tmp มัน หรือ retval ต้องมีเหตุผลที่ดีสำหรับทำเช่นนั้น
Prefer Concrete Names over Abstract Names
ตัวอย่างเช่น สมมติว่าคุณมีเมธอดภายในชื่อ ServerCanStart() ซึ่งทำการทดสอบ
เซิร์ฟเวอร์สามารถฟังบนพอร์ต TCP/IP ที่กำหนดหรือไม่ ชื่อ ServerCanStart() ค่อนข้าง
นามธรรมแม้ว่า ชื่อที่เป็นรูปธรรมมากขึ้นคือ CanListenOnPort() ชื่อนี้โดยตรง
อธิบายว่าวิธีการนี้จะทำอย่างไร
Attaching Extra Information to a Name
หากมีบางสิ่งที่สำคัญมากเกี่ยวกับตัวแปรที่ผู้อ่านต้องรู้ก็คุ้มค่า
แนบ "คำ" พิเศษกับชื่อ
string id; // Example: "af84ef845cd8"
คุณอาจต้องการตั้งชื่อเป็น hex_id แทน หากผู้อ่านต้องจำ ID
Values with Units
ถ้าตัวแปรคุณเป็นหน่วยวัดมันมีประโยชน์มากที่จะใส่ unit
ตัวอย่างเช่น
var start = (new Date()).getTime(); // top of the page
...
var elapsed = (new Date()).getTime() - start; // bottom of the page
document.writeln("Load time was: " + elapsed + " seconds");
var start_ms = (new Date()).getTime(); // top of the page
...
var elapsed_ms = (new Date()).getTime() - start_ms; // bottom of the page
document.writeln("Load time was: " + elapsed_ms / 1000 + " seconds");
Start(int delay ) = delay → delay_secs
CreateCache(int size ) = size → size_mb
ThrottleDownload(float limit) = limit → max_kbps
Rotate(float angle) = angle → degrees_cw
Encoding Other Important Attributes
A password is in “plaintext” and should be encrypted before further
processing
password → plaintext_password
A user-provided comment that needs escaping before being displayed
comment → unescaped_comment
Bytes of html have been converted to UTF-8
html → html_utf8
Incoming data has been “url encoded”
data→data_urlenc
How Long Should a Name Be?
newNavigationControllerWrappingViewControllerForDataSourceOfClass
ยิ่งชื่อยาว ยิ่งจำยาก และยิ่งกินเนื้อที่บนหน้าจอ อาจทำให้มีเส้นเกินมาพันกัน
ในทางกลับกัน โปรแกรมเมอร์สามารถนำคำแนะนำนี้ไปไกลเกินไป โดยใช้เพียงคำเดียว ชื่อ คุณควรจัดการการแลกเปลี่ยนนี้อย่างไร?
คุณตัดสินใจอย่างไรระหว่างการตั้งชื่อ a variable d, days, or days_since_last_update?
Shorter Names Are Okay for Shorter Scope
ถ้าตัวแปรมันมี scope กว้างต้องเป็นชื่อที่ชัดเจนเพื่อความเข้าใจ
Acronyms and Abbreviations (ตัวย่อ)
บางครั้งโปรแกรมเมอร์ใช้คำย่อและตัวย่อเพื่อให้ชื่อมีขนาดเล็ก—สำหรับ ตัวอย่าง การตั้งชื่อคลาส BEManager แทน BackEndManager การหดตัวนี้คุ้มค่าหรือไม่
ความสับสนที่อาจเกิดขึ้น?
ในหนังสือบอกว่าประสบการณ์พวกเขาบอกว่าตัวย่อ project-specific เป็นแนวคิดที่ไม่ดีและคลุมเคลือ
กฎทั่วไปของเราคือ: เพื่อนร่วมทีมใหม่จะเข้าใจความหมายของชื่อหรือไม่? ถ้า
งั้นก็คงจะไม่เป็นไร เช่น str instead of string.
Throwing Out Unneeded Words
เช่น instead of ConvertToString(), the name ToString(), instead of DoServeLoop(), the name ServeLoop()
Use Name Formatting to Convey Meaning (ใช้การจัดรูปแปบบเพื่อสื่อความหมาย)
static const int kMaxOpenFiles = 100;
class LogReader {
public:
void OpenFile(string local_file);
private:
int offset_;
DISALLOW_COPY_AND_ASSIGN(LogReader);
};
การจัดรูปแบบส่วนใหญ่ในตัวอย่างนี้ค่อนข้างธรรมดา—โดยใช้ CamelCase สำหรับชื่อคลาส และ ใช้ lower_separated สำหรับชื่อตัวแปร แต่อนุสัญญาอื่นๆ อาจมี
ทำให้คุณประหลาดใจ
ค่าคงที่อยู่ในรูปแบบ kConstantName แทนที่จะเป็น CONSTANT_NAME มีประโยชน์ในการแยกแยะได้ง่ายจากค่าคงที่ (ค่าคงที่ส่วนใหญ่จะใช้ตัวพิมพ์ใหญ่หมด)
ตัวแปรของสมาชิกคลาสก็เหมือนตัวแปรปกติ แต่ต้องลงท้ายด้วยขีดล่าง เช่น offset_ ทีแรกอนุสัญญานี้อาจดูแปลกแต่สามารถแยกแยะได้ทันที จากตัวแปร member ตัวอย่างเช่น
stats.clear(); คุณอาจสงสัยว่า start เป็นของ class หรือไม่ หากใช้ _ คุณจะรู้ได้ทันที
Other Formatting Conventions
ขึ้นอยู่กับบริบทของโครงการหรือภาษาของคุณ อาจมีการจัดรูปแบบอื่น ข้อตกลงที่คุณสามารถใช้เพื่อทำให้ชื่อมีข้อมูลเพิ่มเติม
constructors ควรจะเป็น capitalized
function ควรจะเริ่มต้นด้วย lowercase letter
var x = new DatePicker(); // DatePicker() is a "constructor" function
var y = pageHeight(); // pageHeight() is an ordinary function
CHAPTER 3 Names That Can’t Be Misconstrued (ระวังชื่อที่อาจเข้าใจผิดได้)
ไตร่ตรองชื่อของคุณอย่างจริงจังโดยถามตัวเองว่า “ความหมายอื่นใดที่สามารถ
มีคนตีความจากชื่อนี้หรือไม่”
เรามาลองดูการตีความผิดของแต่ละชื่อกัน
Example: Filter()
results = Database.all_objects.filter("year <= 2011")
มันไม่ clear ตรงที่ว่าเราจะ "to pick out" or "to get rid of" มันจะดีกว่าถ้าเราไม่ใช้ชื่อ filter เพราะมันเข้าใจผิดได้ง่าย
Prefer min and max for (Inclusive) Limits
CART_TOO_BIG_LIMIT = 10
if shopping_cart.num_items() >= CART_TOO_BIG_LIMIT:
Error("Too many items in cart.")
ปัญหานี้คือการที่ CART_TOO_BIG_LIMIT ไม่ clear ว่า “up to” or “up to and including.” วิธีแก้คือ ใส่ max_ หรือ mix ไว้ข้างหน้า
Prefer first and last for Inclusive Ranges
print integer_range(start=2, stop=4)
# Does this print [2,3] or [2,3,4] (or something else)
เปลี่ยนมาใช้ first , last แทนจะเข้าใจมากกว่า
set.PrintKeys(first="Bart", last="Maggie"
Naming Booleans
ต้องให้แน่ใจว่า boolean จริง ๆ แล้วเป็น true or false
bool read_password = true;
มี 2 ความหมาย
- We need to read the password
- The password has already been read
โดยปกติการเพิ่มคำ is, has, can , should จะทำให้ชัดเจนมากขึ้น
CHAPTER 4 Aesthetics
easy on the eyes
มีหลักการ 3 ข้อ
- ใช้เค้าโครงที่สอดคล้องกัน ด้วยรูปแบบที่ผู้อ่านคุ้นเคย
- ทำให้โค้ดที่คล้ายกันดูคล้ายกัน
- จัดกลุ่ม code
class StatsKeeper {
public:
// A class for keeping track of a series of doubles
void Add(double d); // and methods for quick statistics about them
private: int count; /* how many so far
*/ public:
double Average();
private: double minimum;
list<double>
past_items
;double maximum;
};
ลองดูโค้ดชุดนี้หลังจากปรับปรุงแล้ว
// A class for keeping track of a series of doubles
// and methods for quick statistics about them.
class StatsKeeper {
public:
void Add(double d);
double Average();
private:
list<double> past_items;
int count; // how many so far
double minimum;
double maximum;
};
เห็นได้ชัดว่าการทำงานกับโค้ดที่ดูสวยงามนั้นง่ายกว่า ถ้านึกถึง มันใช้เวลาส่วนใหญ่ในการเขียนโปรแกรมของคุณเพื่อดูโค้ด! ยิ่งคุณอ่านผ่านๆ ได้เร็วเท่าไหร่ รหัสของคุณ ยิ่งทำให้ทุกคนใช้งานได้ง่ายขึ้น
เห็นได้ชัดว่าการทำงานกับโค้ดที่ดูสวยงามนั้นง่ายกว่า ถ้านึกถึง
มันใช้เวลาส่วนใหญ่ในการเขียนโปรแกรมของคุณเพื่อดูโค้ด! ยิ่งคุณอ่านผ่านๆ ได้เร็วเท่าไหร่
รหัสของคุณ ยิ่งทำให้ทุกคนใช้งานได้ง่ายขึ้น
Use Methods to Clean Up Irregularity
DatabaseConnection database_connection;
string error;
assert(ExpandFullName(database_connection, "Doug Adams", &error)
== "Mr. Douglas Adams");
assert(error == "");
assert(ExpandFullName(database_connection, " Jake Brown ", &error)
== "Mr. Jacob Brown III");
assert(error == "");
assert(ExpandFullName(database_connection, "No Such Guy", &error) == "");
assert(error == "no match found");
assert(ExpandFullName(database_connection, "John", &error) == "");
assert(error == "more than one result");
CheckFullName("Doug Adams", "Mr. Douglas Adams", "");
CheckFullName(" Jake Brown ", "Mr. Jake Brown III", "");
CheckFullName("No Such Guy", "", "no match found");
CheckFullName("John", "", "more than one result");
Organize Declarations into Blocks
class FrontendServer {
public:
FrontendServer();
void ViewProfile(HttpRequest* request);
void OpenDatabase(string location, string user);
void SaveProfile(HttpRequest* request);
string ExtractQueryParam(HttpRequest* request, string param);
void ReplyOK(HttpRequest* request, string html);
void FindFriends(HttpRequest* request);
void ReplyNotFound(HttpRequest* request, string error);
void CloseDatabase(string location);
~FrontendServer();
};
class FrontendServer {
public:
FrontendServer();
~FrontendServer();
// Handlers
void ViewProfile(HttpRequest* request);
void SaveProfile(HttpRequest* request);
void FindFriends(HttpRequest* request);
// Request/Reply Utilities
string ExtractQueryParam(HttpRequest* request, string param);
void ReplyOK(HttpRequest* request, string html);
void ReplyNotFound(HttpRequest* request, string error);
// Database Helpers
void OpenDatabase(string location, string user);
void CloseDatabase(string location);
};
Chapter 5 Knowing What to Comment
จุดประสงค์ของการ comment คือเพื่อช่วยให้ผู้อ่านรู้เท่าๆ กับที่ผู้เขียนรู้
- รู้ว่าอะไรไม่ควรออกความเห็น
- บันทึกความคิดของคุณในขณะที่คุณเขียนโค้ด
- สวมบทบาทเป็นผู้อ่าน จินตนาการถึงสิ่งที่พวกเขาต้องรู้
อย่า comment ในข้อเท็จจริงที่ได้มาโดยเร็วจากตัวโค้ดเอง
แต่สำหรับกรณีนี้การเขียน comment จะช่วยให้เข้าใจ code ได้ไวขึ้น
# remove everything after the second '*'
name = '*'.join(line.split('*')[:2])
/ Find a Node with the given 'name' or return NULL.
// If depth <= 0, only 'subtree' is inspected.
// If depth == N, only 'subtree' and N levels below are inspected.
Node* FindNodeInSubtree(Node* subtree, string name, int depth);
Don’t Comment Bad Names—Fix the Names Instead อย่า comment อธิบายชื่อที่แย่
// Enforce limits on the Reply as stated in the Request,
// such as the number of items returned, or total byte size, etc.
void CleanReply(Request request, Reply reply);
Comment on Your Constants
เมื่อคุณกำหนดค่าคงที่ควรบอก story มันด้วยว่ามายังไง
NUM_THREADS = 8 # as long as it's >= 2 * num_processors, that's good enough.
// Impose a reasonable limit - no human can read that much anyway.
const int MAX_RSS_SUBSCRIPTIONS = 1000;
image_quality = 0.72; // users thought 0.72 gave the best size/quality tradeoff
SHOULD YOU COMMENT THE WHAT, THE WHY, OR THE HOW
Chapter 6 Making Comments Precise and Compact (แม่นยำและกระชับ)
comment ควรมีอัตราส่วนข้อมูลต่อพื้นที่สูง
Keep Comments Compact
// The int is the CategoryType.
// The first float in the inner pair is the 'score',
// the second is the 'weight'.
typedef hash_map<int, pair<float, float> > ScoreMap;
เขียนให้กระชับ
// CategoryType -> (score, weight)
typedef hash_map<int, pair<float, float> > ScoreMap;
Avoid Ambiguous Pronouns
ดูตัวอย่างต่อไปนี้ "it" or "this" สื่อถึงอะไร
// Insert the data into the cache, but check if it's too big first.
ใน comment it อาจสื่อถึง data or cache แก้ไขได้ด้วยเปลี่ยน it เป็น the data
// Insert the data into the cache, but check if the data is too big first.
ลองเปลี่ยนให้ดีกว่าเดิม
// If the data is small enough, insert it into the cache.
Describe Function Behavior Precisely
// Return the number of lines in this file.
int CountLines(string filename) { ... }
comment ข้างบนโคตรไม่เคลียเพราะว่ามีหลายทางกำหนด line เช่น
- "" (an empty file)—0 or 1 line?
- "hello"—0 or 1 line?
- "hello\n"—1 or 2 lines?
- "hello\n world"—1 or 2 lines?
- "hello\n\r cruel\n world\r"—2, 3, or 4 lines?
ลองเปลี่ยนมาเป็น comment ข้างล่างนี้จะดูดีกว่า
// Count how many newline bytes ('\n') are in the file.
int CountLines(string filename) { ... }
Path II Simplifying Loops and Logic
ถ้าโค้ดไม่มีเงื่อนไข ลูป หรือคำสั่งควบคุมโฟลว์อื่นๆ มันจะง่ายมากอ่าน. การกระโดดและกิ่งก้านเหล่านี้เป็นสิ่งที่ยาก ซึ่งโค้ดอาจสร้างความสับสนได้อย่างรวดเร็ว
บทนี้เกี่ยวกับการทำให้ขั้นตอนการควบคุมในโค้ดของคุณอ่านง่าย
Chapter 7 Making Control Flow Easy to Read
ถ้าโค้ดไม่มีเงื่อน ลูปหรือคำสั่วคุมโฟลว์มันจะอ่านง่ายมากๆ
The Order of Arguments in Conditionals
if (length >= 10) or if (10 <= length)
while (bytes_received < bytes_expected) or
while (bytes_expected > bytes_received)
ตัดสินใจยังไงดีว่าจะเขียน a < b หรือ b > a ?
แนวทางตรงกับการใช้ภาษาอังกฤษมันเป็นธรรมชาติที่จะพูดว่า ถ้าหากคุณทำเงินได้อย่างน้อย $100K/years or ถ้าหากคุณอายุน้อยไมต่ำกว่า18ปี
สิ่งนี้อธิบายว่าทำไม (bytes_received < bytes_expected) อ่านง่ายกว่า
bytes_received คือค่าที่เราจะตรวจสอบและมันก็เพิ่มขึ้นเมื่อโปรแกรมทำงาน
bytes_expected คือที่คงที่
The Order of if/else Blocks
if (a == b) {
// Case One ...
} else {
// Case Two ...
}
หรือ
if (a != b) {
// Case Two ...
} else {
// Case One ...
}
คุณอาจไม่เคยคิดมากเกี่ยวกับเรื่องนี้มาก่อน แต่ในบางกรณีก็มีข้อดี เหตุผลที่ชอบคำสั่งหนึ่งมากกว่าที่อื่น:
- ชอบจัดการกับกรณีบวกก่อนแทนที่จะเป็นค่าลบ—เช่น if (debug) แทนของ if (!debug)
- ให้จัดการกับกรณีที่ง่ายกว่าก่อนเพื่อเอามันออกไปให้พ้นทาง วิธีนี้อาจจะอนุญาตให้ทั้ง if และ else ปรากฏบนหน้าจอพร้อมกันซึ่งเป็นสิ่งที่ดี
- ชอบจัดการกับคดีที่น่าสนใจหรือเห็นได้ชัดกว่าก่อน
การตั้งอะไรสักอย่างเป็น first condtion เป็นอะไรที่ให้ความสำคัญและน่าสนใจมากกว่าเสมอ
แทนที่จะลดจำนวนบรรทัด เมตริกที่ดีกว่าคือการลดเวลาต้องการใครสักคนที่จะเข้าใจมัน
The ternary ?: ควรใช้กับ simple cases
Avoid do/while Loops
เพราะว่าปกติเราอ่านโค้ดจากบนลงล่าง
Returning Early from a Function
ผู้เขียนโค้ดบางคนเชื่อว่าฟังก์ชันไม่ควรมีคำสั่งส่งคืนหลายรายการ เป็นเรื่องไร้สาร การ return ก่อนก็ไม่ได้เป็นเรื่องเสียหายตัวอย่างเช่น
public boolean Contains(String str, String substr) {
if (str == null || substr == null) return false;
if (substr.equals("")) return true
}
การใช้ฟังก์ชันนี้โดยไม่มี "guard clauses" เหล่านี้จะผิดธรรมชาติมาก
Minimize Nesting
รหัสที่ซ้อนกันอย่างลึกซึ้งนั้นยากที่จะเข้าใจรังแต่ละระดับจะผลักดันเงื่อนไขพิเศษให้
if (user_result == SUCCESS) {
if (permission_result != SUCCESS) {
reply.WriteErrors("error reading permissions");
reply.Done();
return;
}
reply.WriteErrors("");
} else {
reply.WriteErrors(user_result);
}
reply.Done();
แบบนี้จะดูดีกว่า
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();
Can You Follow the Flow of Execution?
บทนี้จะกล่างถึง flow การไหลของโปรแกรม ในทางปฎิบัติเบื้องหลังโค้ดทำงานยากที่จะไล่ให้เข้าตัวอย่างเช่น
threading → ไม่ชัดเจนว่าโค้ดใดที่รันเมื่อใด
signal/interrupt handlers → โค้ดบางโค้ดอาจถูกเรียกใช้เมื่อใดก็ได้
exceptions → การดำเนินการสามารถเกิดขึ้นได้ผ่านการเรียกใช้ฟังก์ชันหลายฟังก์ชัน
function pointers & anonymous functions → เป็นการยากที่จะรู้ว่าโค้ดใดที่จะรันเพราะไม่รู้ในเวลารวบรวม
virtual methods → object.virtualMethod() อาจเรียกใช้โค้ดของคลาสย่อยที่ไม่รู้จัก
โครงสร้างเหล่านี้บางส่วนมีประโยชน์มาก และยังทำให้โค้ดของคุณอ่านง่ายขึ้นอีกด้วย และซ้ำซากน้อยลง แต่ในฐานะโปรแกรมเมอร์ บางครั้งเราก็หลงทางและใช้มัน
มากเกินไปโดยไม่ทราบว่าผู้อ่านจะเข้าใจโค้ดในภายหลังได้ยากเพียงใด นอกจากนี้ โครงสร้างเหล่านี้ยังทำให้บั๊กติดตามได้ยากขึ้นมาก
Chapter 8 Breaking Down Giant Expressions ทำลาย Expressions ใหญ่ซะ
ปลาหมึกยักษ์เป็นสัตว์ที่น่าทึ่งและฉลาด แต่มีการออกแบบตัวที่เกือบจะสมบูรณ์แบบมีหนึ่งตัว ข้อบกพร่องร้ายแรง: มีสมองรูปโดนัทที่พันรอบหลอดอาหาร ดังนั้นถ้ามันกลืนเกินไป
อาหารมากในคราวเดียวก็ทำให้สมองเสียหายได้
การวิจัยล่าสุดชี้ให้เห็นว่าพวกเราส่วนใหญ่คิดได้เพียง สามหรือสี่อย่างในคราวเดียว* พูดง่ายๆ ก็คือ ยิ่งการแสดงออกของรหัสมากเท่าไหร่ก็ยิ่งยากขึ้นเท่านั้น
จะได้เข้าใจ
Explaining Variables
if line.split(':')[0].strip() == "root":
เขียนให้ดูขึ้น
username = line.split(':')[0].strip()
if username == "root":
Summary Variables
ถ้า expression ไม่ต้องอธิบายเพราะเข้าใจอยู่แล้ว เราสามารถเอา expression ตัวนั้นมาทำ summary variable แล้วมันจะจัดการง่ายขึ้น
if (request.user.id == document.owner_id) {
// user can edit this document...
}
if (request.user.id != document.owner_id) {
// document is read-only...
}
final boolean user_owns_document = (request.user.id == document.owner_id);
if (user_owns_document) {
// user can edit this document...
}
...
if (!user_owns_document) {
// document is read-only...
}
Using De Morgan’s Laws
- not (a or b or c) ⇔ (not a) and (not b) and (not c)
- not (a and b and c) ⇔ (not a) or (not b) or (not c) หากคุณมีปัญหาในการจำกฎหมายเหล่านี้ สรุปง่ายๆ คือ “แจกจ่ายไม่และเปลี่ยน and/or” (หรือไปทางอื่นคุณ "แยกเอาสิ่งที่ไม่")
ในบางครั้ง คุณสามารถใช้กฎเหล่านี้เพื่อทำให้นิพจน์บูลีนอ่านได้ง่ายขึ้น ตัวอย่างเช่น
if (!(file_exists && !is_protected)) Error("Sorry, could not read file.");
เขียนใหม่เป็น
if (!file_exists || is_protected) Error("Sorry, could not read file.");
Chapter 9 Variables and Readability
ในบทนี้ คุณจะเห็นว่าการใช้ตัวแปรอย่างเลอะเทอะทำให้โปรแกรมเข้าใจยากขึ้นได้อย่างไร โดยเฉพาะอย่างยิ่ง มีปัญหาสามประการที่ต้องต่อสู้ด้วย
- ยิ่งมีตัวแปรมากเท่าใด การติดตามทั้งหมดก็ยิ่งยากขึ้นเท่านั้น
- ยิ่งมีขอบเขตของตัวแปรมากเท่าไร คุณก็ยิ่งต้องติดตามมันนานขึ้นเท่านั้น
- ยิ่งตัวแปรเปลี่ยนแปลงบ่อยเท่าไหร่ ยิ่งติดตามค่าปัจจุบันได้ยากขึ้นเท่านั้น
Eliminating Variables
ในส่วนนี้ เราสนใจที่จะกำจัดตัวแปรที่ไม่ปรับปรุงความสามารถในการอ่าน เมื่อไหร่ ตัวแปรแบบนี้ถูกลบออก โค้ดใหม่มีความกระชับและเข้าใจง่ายขึ้น ในส่วนต่อไปนี้คือตัวอย่างบางส่วนของการแสดงตัวแปรที่ไม่จำเป็นเหล่านี้
ทำให้ตัวแปรของคุณมองเห็นได้ด้วยโค้ดไม่กี่บรรทัดเท่าที่เป็นไปได้
ลองดูตัวอย่างต่อไปนี้
class LargeClass {
string str_;
void Method1() {
str_ = ...;
Method2();
}
void Method2() {
// Uses str_
}
// Lots of other methods that don't use str_ ...
};
ในบางแง่มุม ตัวแปรสมาชิกของคลาสก็เหมือนกับ "มินิโกลบอล" ภายในขอบเขตของคลาส สำหรับโดยเฉพาะอย่างยิ่งคลาสขนาดใหญ่ เป็นการยากที่จะติดตามตัวแปรสมาชิกทั้งหมดและวิธีการใด แก้ไขแต่ละรายการ มินิโกลบอลน้อยยิ่งดี
class LargeClass {
void Method1() {
string str = ...;
Method2(str);
}
void Method2(string str) {
// Uses str
}
// Now other methods can't see str.
};
Prefer Write-Once Variables
ในบทนี้ เราได้พูดคุยกันถึงวิธีการทำความเข้าใจโปรแกรมที่มีจำนวนมาก . ได้ยากขึ้น ตัวแปร "ในการเล่น" การคิดถึงตัวแปรที่เปลี่ยนแปลงอยู่ตลอดเวลานั้นยากยิ่งกว่า
การติดตามค่านิยมของพวกเขาจะเพิ่มระดับความยากเป็นพิเศษ เพื่อต่อสู้กับปัญหานี้ เรามีข้อเสนอแนะที่อาจฟังดูแปลก ๆ เล็กน้อย: ชอบเขียนตัวแปรครั้งเดียว
static const int NUM_THREADS = 10;
ยิ่งมีการจัดการตัวแปรสถานที่มากเท่าไร ก็ยิ่งให้เหตุผลกับมันยากขึ้นเท่านั้น
Path III Reorganizing Your Code
ในส่วนนี้ เราจะพูดถึงการเปลี่ยนแปลงครั้งใหญ่ที่คุณสามารถทำกับโค้ดของคุณได้ที่ระดับฟังก์ชันโดยเฉพาะอย่างยิ่ง เราจะพูดถึงสามวิธีในการจัดระเบียบโค้ดของคุณใหม่
- แยก “ปัญหาย่อยที่ไม่เกี่ยวข้อง” ที่ไม่เกี่ยวข้องกับเป้าหมายหลักของโปรแกรมของคุณ
- จัดเรียงรหัสของคุณใหม่เพื่อให้ทำงานครั้งละหนึ่งงานเท่านั้น
- อธิบายรหัสของคุณเป็นคำก่อน และใช้คำอธิบายนี้เพื่อช่วยแนะนำ
Chapter 10 Extracting Unrelated Subproblems การแยกปัญหาย่อยที่ไม่เกี่ยวข้อง
วิศวกรรมเป็นเรื่องเกี่ยวกับการแบ่งปัญหาใหญ่ออกเป็นปัญหาเล็ก ๆ การแก้ปัญหาเหล่านั้นกลับมารวมกัน การนำหลักการนี้ไปใช้กับโค้ดจะทำให้มีความแข็งแกร่งและอ่านง่ายขึ้น
ปัญหาย่อยที่เราหมายถึงคือ
- ดูหน้าที่หรือบล็อกของรหัสที่กำหนด แล้วถามตัวเองว่า “What is the high-level goal of code“
- แต่ละบรรทัด ถามตัวเองว่า “มันทำงานตรงไปยังเป้าหมายนั้นหรือไม่? หรือเป็นการแก้ปัญหาที่ไม่เกี่ยวข้องกัน ปัญหาย่อยจำเป็นต้องพบหรือไม่”
หากมีบรรทัดเพียงพอในการแก้ปัญหาย่อยที่ไม่เกี่ยวข้อง ให้แยกรหัสนั้นแยกเป็นฟังก์ชัน
อธิบายว่าโค้ดใดที่ต้องทำเป็นภาษาอังกฤษธรรมดา เหมือนกับที่คุณทำกับเพื่อนร่วมงาน
ให้ความสนใจกับคำและวลีสำคัญที่ใช้ในคำอธิบายนี้
เขียนโค้ดของคุณให้ตรงกับคำอธิบายนี้
Chapter 13 Writing Less Code
The most readable code is no code at all.
เดียวมาจดต่อ..
Top comments (0)