DEV Community

Nantipat
Nantipat

Posted on • Edited on

จด The Art of Readable Code

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");
Enter fullscreen mode Exit fullscreen mode

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); 
};
Enter fullscreen mode Exit fullscreen mode

การจัดรูปแบบส่วนใหญ่ในตัวอย่างนี้ค่อนข้างธรรมดา—โดยใช้ 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
Enter fullscreen mode Exit fullscreen mode

CHAPTER 3 Names That Can’t Be Misconstrued  (ระวังชื่อที่อาจเข้าใจผิดได้)

ไตร่ตรองชื่อของคุณอย่างจริงจังโดยถามตัวเองว่า “ความหมายอื่นใดที่สามารถ
มีคนตีความจากชื่อนี้หรือไม่”

เรามาลองดูการตีความผิดของแต่ละชื่อกัน

Example: Filter()

results = Database.all_objects.filter("year <= 2011")
Enter fullscreen mode Exit fullscreen mode

มันไม่ 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.")
Enter fullscreen mode Exit fullscreen mode

ปัญหานี้คือการที่  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)
Enter fullscreen mode Exit fullscreen mode

เปลี่ยนมาใช้ first , last แทนจะเข้าใจมากกว่า

set.PrintKeys(first="Bart", last="Maggie"
Enter fullscreen mode Exit fullscreen mode

Naming Booleans

ต้องให้แน่ใจว่า boolean จริง ๆ แล้วเป็น true or false

bool read_password = true;
Enter fullscreen mode Exit fullscreen mode

มี 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; 
};
Enter fullscreen mode Exit fullscreen mode

ลองดูโค้ดชุดนี้หลังจากปรับปรุงแล้ว

// 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; 
};
Enter fullscreen mode Exit fullscreen mode

เห็นได้ชัดว่าการทำงานกับโค้ดที่ดูสวยงามนั้นง่ายกว่า ถ้านึกถึง มันใช้เวลาส่วนใหญ่ในการเขียนโปรแกรมของคุณเพื่อดูโค้ด! ยิ่งคุณอ่านผ่านๆ ได้เร็วเท่าไหร่ รหัสของคุณ ยิ่งทำให้ทุกคนใช้งานได้ง่ายขึ้น

เห็นได้ชัดว่าการทำงานกับโค้ดที่ดูสวยงามนั้นง่ายกว่า ถ้านึกถึง
มันใช้เวลาส่วนใหญ่ในการเขียนโปรแกรมของคุณเพื่อดูโค้ด! ยิ่งคุณอ่านผ่านๆ ได้เร็วเท่าไหร่
รหัสของคุณ ยิ่งทำให้ทุกคนใช้งานได้ง่ายขึ้น

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");
Enter fullscreen mode Exit fullscreen mode
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");
Enter fullscreen mode Exit fullscreen mode

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(); 
};
Enter fullscreen mode Exit fullscreen mode
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); 
};
Enter fullscreen mode Exit fullscreen mode

Chapter 5 Knowing What to Comment

จุดประสงค์ของการ comment คือเพื่อช่วยให้ผู้อ่านรู้เท่าๆ กับที่ผู้เขียนรู้

  • รู้ว่าอะไรไม่ควรออกความเห็น
  • บันทึกความคิดของคุณในขณะที่คุณเขียนโค้ด
  • สวมบทบาทเป็นผู้อ่าน จินตนาการถึงสิ่งที่พวกเขาต้องรู้

อย่า comment ในข้อเท็จจริงที่ได้มาโดยเร็วจากตัวโค้ดเอง

แต่สำหรับกรณีนี้การเขียน comment จะช่วยให้เข้าใจ code ได้ไวขึ้น

# remove everything after the second '*' 
name = '*'.join(line.split('*')[:2])
Enter fullscreen mode Exit fullscreen mode
/ 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);
Enter fullscreen mode Exit fullscreen mode

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);
Enter fullscreen mode Exit fullscreen mode

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

Enter fullscreen mode Exit fullscreen mode

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;
Enter fullscreen mode Exit fullscreen mode

เขียนให้กระชับ

// CategoryType -> (score, weight) 
typedef hash_map<int, pair<float, float> > ScoreMap;
Enter fullscreen mode Exit fullscreen mode

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) { ... }
Enter fullscreen mode Exit fullscreen mode

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) { ... }
Enter fullscreen mode Exit fullscreen mode

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 ... 
}
Enter fullscreen mode Exit fullscreen mode

หรือ

if (a != b) { 
 // Case Two ... 
} else { 
 // Case One ... 
}
Enter fullscreen mode Exit fullscreen mode

คุณอาจไม่เคยคิดมากเกี่ยวกับเรื่องนี้มาก่อน แต่ในบางกรณีก็มีข้อดี เหตุผลที่ชอบคำสั่งหนึ่งมากกว่าที่อื่น:

  • ชอบจัดการกับกรณีบวกก่อนแทนที่จะเป็นค่าลบ—เช่น 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
}
Enter fullscreen mode Exit fullscreen mode

การใช้ฟังก์ชันนี้โดยไม่มี "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();
Enter fullscreen mode Exit fullscreen mode

แบบนี้จะดูดีกว่า

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();
Enter fullscreen mode Exit fullscreen mode

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":

Enter fullscreen mode Exit fullscreen mode

เขียนให้ดูขึ้น

username = line.split(':')[0].strip() 
if username == "root":
Enter fullscreen mode Exit fullscreen mode

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... 
}
Enter fullscreen mode Exit fullscreen mode
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... 
}
Enter fullscreen mode Exit fullscreen mode

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.");
Enter fullscreen mode Exit fullscreen mode

เขียนใหม่เป็น

if (!file_exists || is_protected) Error("Sorry, could not read file.");
Enter fullscreen mode Exit fullscreen mode

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_ ... 
};
Enter fullscreen mode Exit fullscreen mode

ในบางแง่มุม ตัวแปรสมาชิกของคลาสก็เหมือนกับ "มินิโกลบอล" ภายในขอบเขตของคลาส สำหรับโดยเฉพาะอย่างยิ่งคลาสขนาดใหญ่ เป็นการยากที่จะติดตามตัวแปรสมาชิกทั้งหมดและวิธีการใด แก้ไขแต่ละรายการ มินิโกลบอลน้อยยิ่งดี

class LargeClass { 
 void Method1() { 
 string str = ...; 
 Method2(str); 
 } 
 void Method2(string str) { 
 // Uses str 
 } 
 // Now other methods can't see str. 
};
Enter fullscreen mode Exit fullscreen mode

Prefer Write-Once Variables
ในบทนี้ เราได้พูดคุยกันถึงวิธีการทำความเข้าใจโปรแกรมที่มีจำนวนมาก . ได้ยากขึ้น ตัวแปร "ในการเล่น" การคิดถึงตัวแปรที่เปลี่ยนแปลงอยู่ตลอดเวลานั้นยากยิ่งกว่า
การติดตามค่านิยมของพวกเขาจะเพิ่มระดับความยากเป็นพิเศษ เพื่อต่อสู้กับปัญหานี้ เรามีข้อเสนอแนะที่อาจฟังดูแปลก ๆ เล็กน้อย: ชอบเขียนตัวแปรครั้งเดียว

static const int NUM_THREADS = 10;
Enter fullscreen mode Exit fullscreen mode

ยิ่งมีการจัดการตัวแปรสถานที่มากเท่าไร ก็ยิ่งให้เหตุผลกับมันยากขึ้นเท่านั้น

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)