Topic 44 Naming Things

จุดเริ่มต้นของความฉลาด คือการเรียกสิ่งต่างๆ ด้วยชื่อที่ถูกต้อง

ขงจื๊อ (Confucius)

ชื่อนั้นสำคัญไฉน? เมื่อเราเขียนโปรแกรม คำตอบคือ “มันคือทุกอย่างเลยล่ะ!”

เราตั้งชื่อให้กับ applications, subsystems, modules, functions, variables—เราสร้างสิ่งใหม่ๆ และมอบชื่อให้พวกมันอยู่ตลอดเวลา และชื่อเหล่านั้นก็สำคัญมากๆ เพราะพวกมันบ่งบอกถึงความตั้งใจ (intent) และความเชื่อของคุณได้เยอะเลยทีเดียว

เราเชื่อว่าสิ่งต่างๆ ควรตั้งชื่อตามบทบาทของมันในโค้ดของคุณ ซึ่งหมายความว่า เมื่อไหร่ก็ตามที่คุณสร้างอะไรบางอย่างขึ้นมา คุณต้องหยุดและคิดว่า “แรงจูงใจในการสร้างสิ่งนี้ขึ้นมาคืออะไร?”

นี่เป็นคำถามที่ทรงพลังมาก เพราะมันจะดึงคุณออกจากโหมดการแก้ปัญหาเฉพาะหน้า แล้วทำให้คุณมองภาพรวมที่กว้างขึ้น เมื่อคุณพิจารณาบทบาทของตัวแปรหรือฟังก์ชัน คุณกำลังคิดว่ามันพิเศษตรงไหน มันทำอะไรได้บ้าง และมันมีปฏิสัมพันธ์กับอะไรบ้าง บ่อยครั้งที่เราเพิ่งมาตระหนักได้ว่าสิ่งที่กำลังจะทำมันดูไม่มีเหตุผลเอาซะเลย ก็เพียงเพราะเราหาชื่อที่เหมาะสมให้มันไม่ได้นี่แหละ

มีงานวิจัยที่บอกว่าชื่อสื่อความหมายได้ลึกซึ้งนะ ปรากฏว่าสมองคนเราอ่านและทำความเข้าใจคำพูดได้เร็วมาก เร็วกว่ากิจกรรมอื่นๆ เยอะเลย นี่หมายความว่าพวกคำต่างๆ จะมีลำดับความสำคัญค่อนข้างสูงเวลาที่เราพยายามทำความเข้าใจอะไรสักอย่าง เราพิสูจน์เรื่องนี้ได้ด้วย Stroop effect.[67]

ลองดูแผงด้านล่างนี้ดูสิครับ มันมีรายการชื่อสีหรือเฉดสีอยู่ โดยแต่ละชื่อแสดงด้วยสีหรือเฉดต่างๆ แต่ชื่อกับสีที่ใช้มันอาจจะไม่ตรงกันนะ นี่คือส่วนแรกของความท้าทายนี้—ลองพูดชื่อของแต่ละสีตามที่เขียนไว้ออกมาดังๆ ดูครับ:[68]

A figure shows the names of various colors such as, magenta, blue, red, green, yellow, cyan, and black. The color of the fonts may or may not represent the name of the color.

คราวนี้ลองใหม่อีกที แต่ให้พูดสีที่ใช้เขียนคำนั้นออกมาแทน ยากขึ้นใช่ไหมล่ะ? มันง่ายนะที่จะอ่านแบบลื่นไหล แต่ยากกว่าเยอะถ้าจะพยายามแยกแยะสีออกมา

สมองของคุณมองว่าคำที่เขียนออกมาคือสิ่งที่ต้องให้ความสำคัญ เราเลยต้องเมคชัวร์ว่าชื่อที่เราใช้ในโค้ดมีคุณค่าพอกับเรื่องนี้

มาดูตัวอย่างสักเล็กน้อยกันครับ:

  • เรากำลังพิสูจน์ตัวตนคนที่จะเข้าเว็บไซต์ขายเครื่องประดับที่ทำจากการ์ดจอเก่าๆ ของเรา:
let user = authenticate(credentials)

ตัวแปรถูกตั้งชื่อว่า user เพราะมันมักจะเป็นแบบนี้แหละ แต่ทำไมล่ะ? มันไม่สื่อความหมายเลย ลองใช้ customer หรือ buyer ดูสิ แบบนี้โค้ดจะคอยย้ำเตือนเราอยู่ตลอดว่าคนๆ นี้กำลังพยายามทำอะไร และเขามีความหมายยังไงสำหรับเรา

  • เรามี instance method ที่ลดราคาคำสั่งซื้อ:
​public​ ​void​ deductPercent(​double​ amount)
​// ...​

มีสองอย่างที่น่าสนใจตรงนี้ครับ อย่างแรกคือ deductPercent บอกว่ามัน "ทำอะไร" แต่ไม่ได้บอกว่า "ทำไปทำไม" ส่วนชื่อพารามิเตอร์อย่าง amount ก็น่าสับสนสุดๆ ว่ามันเป็นจำนวนเงินหรือเป็นเปอร์เซ็นต์กันแน่?

บางทีแบบนี้น่าจะดีกว่า:

​public​ ​void​ applyDiscount(Percentage discount)
​// ...​

ชื่อเมธอดแบบนี้ทำให้ "ความตั้งใจ" (intent) ชัดเจนขึ้น เรายังเปลี่ยนพารามิเตอร์จาก double มาเป็น Percentage ซึ่งเป็น type ที่เรากำหนดขึ้นมาเองด้วย ผมไม่รู้คุณเป็นเหมือนกันไหมนะ แต่เวลาจัดการกับเปอร์เซ็นต์ทีไร ไม่เคยแน่ใจเลยว่ามันควรจะเป็น 0 ถึง 100 หรือ 0.0 ถึง 1.0 การใช้ type มาช่วยแบบนี้มันเหมือนการทำเอกสารกำกับไปในตัวเลยว่าฟังก์ชันนี้คาดหวังอะไร

  • เรามีโมดูล (module) ที่ทำงานสนุกๆ กับตัวเลข Fibonacci หนึ่งในฟังก์ชันที่มีคือการคำนวณหาลำดับที่ n powered th ในอนุกรม ลองหยุดคิดดูหน่อยครับว่าคุณจะเรียกชื่อฟังก์ชันนี้ว่าอะไร

คนส่วนใหญ่ที่พวกเราถามจะเรียกมันว่า fib ซึ่งฟังดูเข้าท่าดีนะ แต่จำไว้ว่าปกติมันจะถูกเรียกใช้งานในบริบทของโมดูล ดังนั้นการเรียกใช้จะเป็น Fib.fib(n) แทนที่จะเป็นอย่างนั้น ลองเปลี่ยนชื่อเป็น of หรือ nth ดีไหม:

Fib.of(0) ​# => 0​
Fib.nth(20) ​# => 4181​

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

แต่อย่างว่าแหละครับ ไม่ใช่ว่าทุกชื่อจะต้องเป็นผู้ท้าชิงรางวัลวรรณกรรมเสมอไป

ข้อยกเว้นที่พิสูจน์กฎเกณฑ์

ในขณะที่เราพยายามเพื่อความชัดเจนในโค้ด แต่เรื่อง "การสร้างแบรนด์" (branding) มันเป็นคนละเรื่องกันเลย

มันมีธรรมเนียมที่ปฏิบัติกันมานานว่าโปรเจกต์หรือทีมโปรเจกต์ควรมีชื่อที่ดูลึกลับ หรือ "ล้ำๆ" หน่อย อย่างชื่อ โปเกมอน, ซูเปอร์ฮีโร่มาร์เวล, สัตว์เลี้ยงลูกด้วยนมที่น่ารักๆ หรือตัวละครใน Lord of the Rings อะไรก็ว่าไป

ตามนั้นจริงๆ ครับ

เคารพวัฒนธรรม

หนังสือเริ่มต้นคอมพิวเตอร์ส่วนใหญ่จะสั่งห้ามไม่ให้คุณใช้ตัวแปรตัวเดียวอย่าง i, j หรือ k.[69]

เราคิดว่ามันก็ไม่ถูกซะทีเดียว

จริงๆ แล้วมันขึ้นอยู่กับ "วัฒนธรรม" ของแต่ละภาษาหรือสภาพแวดล้อมนั้นๆ มากกว่า อย่างในภาษา C เราจะเห็น i, j และ k ถูกใช้เป็นตัวแปรสำหรับวนลูป (loop) เป็นปกติ หรือ s ถูกใช้แทนสตริง (string) เป็นต้น ถ้าคุณเขียนโค้ดในสภาวะแวดล้อมนั้น คุณก็คงคุ้นชินกับมัน และมันจะดูขัดตาเอามากๆ (ซึ่งนั่นคือสิ่งที่ไม่ควรทำ) หากคุณไปทำลายบรรทัดฐานพวกนี้ ในทางกลับกัน การเอาธรรมเนียมแบบนั้นไปใช้ในที่อื่นที่เขาไม่ทำกันมันก็ผิดเหมือนกัน คุณคงไม่ทำอะไรที่ "น่าขนลุก" อย่างตัวอย่างในภาษา Clojure นี้หรอก ที่มีการกำหนดสตริงให้กับตัวแปร i:

(​let​ [i ​"Hello World"​]
(println i))

บางสังคมจะนิยมใช้ camelCase ที่มีตัวพิมพ์ใหญ่ตรงกลาง ขณะที่บางที่อาจชอบ snake_case ที่ใช้ underscore คั่นระหว่างคำมากกว่า ตัวภาษาเองน่ะมันรับได้ทั้งสองแบบแหละครับ แต่นั่นไม่ได้หมายความว่ามันจะถูกกาลเทศะเสมอไป จง "เคารพวัฒนธรรมท้องถิ่น" ของภาษานั้นๆ

บางภาษายอมให้ใช้ Unicode ในชื่อได้ด้วยนะ แต่ลองดูก่อนว่าคนในคอมมูนิตี้เขาทำกันแบบไหน ก่อนจะไปตั้งชื่อน่ารักๆ แบบ ɹǝsn หรือ εξέρχεται

ความสม่ำเสมอ (Consistency)

Emerson โด่งดังจากประโยคที่ว่า “ความสม่ำเสมอที่โง่เขลาคือปีศาจร้ายของจิตใจที่เล็กแคบ…” แต่ว่า Emerson ไม่ได้เป็นหนึ่งในทีมโปรแกรมเมอร์นี่ครับ

ทุกโปรเจกต์ต่างก็มีคำศัพท์เฉพาะตัว มีคำแสลงที่สื่อความหมายพิเศษกันแค่ภายในทีม อย่างคำว่า “Order” (คำสั่งซื้อ/ลำดับ) สำหรับทีมขายของออนไลน์ก็อย่างหนึ่ง แต่สำหรับทีมที่ทำแอปเกี่ยวกับลำดับวงศ์ตระกูลกลุ่มศาสนาความหมายก็คนละเรื่องกันเลย สิ่งสำคัญคือทุกคนในทีมต้องเข้าใจความหมายที่ตรงกัน และใช้มันอย่าง "สม่ำเสมอ" (consistently)

วิธีหนึ่งคือการส่งเสริมให้คุยกันเยอะๆ ครับ ถ้าทุกคนทำ pair programming และสลับคู่กันบ่อยๆ คำศัพท์เฉพาะพวกนี้ก็จะค่อยๆ ซึมซับถึงกันไปเองเหมือนการแพร่ผ่าน (osmosis) เลยล่ะ

อีกวิธีคือการทำพจนานุกรมคำศัพท์โปรเจกต์ (project glossary) ไว้ ลิสต์พวกคำที่มีความหมายพิเศษสำหรับทีมออกมา มันอาจจะเป็นแค่เอกสารง่ายๆ บนวิกิ (wiki) หรือแม้แต่จะเป็นการ์ดจดบันทึกแปะไว้บนผนังก็ได้

พอผ่านไปสักพัก คำแสลงเหล่านั้นจะเริ่มมีชีวิตของมันเอง เมื่อทุกคนเริ่มคุ้นเคยกับคำศัพท์ คุณจะสามารถใช้มันเป็นตัวย่อเพื่อสื่อความหมายที่ลึกซึ้งได้อย่างถูกต้องและกระชับ (ซึ่งนี่แหละครับ คือหัวใจของภาษาที่เป็นแพทเทิร์น หรือ pattern language)

การเปลี่ยนชื่อน่ะยากกว่าเสียอีก

ไม่ว่าคุณจะทุ่มเทแรงกายไปแค่ไหนในช่วงแรก สิ่งต่างๆ มันเปลี่ยนไปได้เสมอ โค้ดถูก refactor การใช้งานเปลี่ยนไป หรือแม้แต่ความหมายก็อาจจะบิดเบือนไปได้เล็กน้อย ถ้าคุณไม่คอยหมั่นอัปเดตชื่อตามไปด้วย คุณอาจจะหลงเข้าไปในฝันร้ายที่แย่ยิ่งกว่าชื่อที่ไม่มีความหมายเสียอีก นั่นก็คือ "ชื่อที่ทำให้เข้าใจผิด" (misleading names) เคยเจอคนอธิบายความลักลั่นในโค้ดอย่าง "ฟังก์ชันที่ชื่อ getData จริงๆ แล้วมันใช้สำหรับเขียนข้อมูลลงไฟล์ archive น่ะ" บ้างไหมครับ?

อย่างที่เราได้พูดคุยกันใน Topic 3 เรื่อง ​_Software Entropy_​ เมื่อไหร่ที่คุณเห็นปัญหา ให้รีบแก้ไขมันเสียเดี๋ยวนี้เลยครับ เมื่อคุณเห็นชื่อที่มันสื่อความหมายไม่ตรงกับความตั้งใจ หรือเป็นชื่อที่ทำให้สับสนหรือเข้าใจผิด ก็จงแก้ไขมันเสีย เพราะเรามีการทำ regression tests ไว้อยู่แล้ว ดังนั้นเราจะสามารถหาจุดที่เราอาจจะพลาดไปเจอได้ไม่ยาก

Tip 74 Name Well; Rename When Needed

ถ้าด้วยเหตุผลอะไรสักอย่างที่คุณไม่สามารถเปลี่ยนชื่อที่มันผิดไปแล้วได้ล่ะก็ นั่นหมายความว่าคุณกำลังเจอโจทย์ที่ยากกว่าเดิมแล้วล่ะครับ: นั่นคือการละเมิดหลักการ ETC (Easy To Change) (ลองไปดูที่ Topic 8 เรื่อง ​_The Essence of Good Design_​ นะครับ) ให้แก้ไขเรื่องนั้นก่อน แล้วค่อยกลับมาเปลี่ยนชื่อเจ้าปัญหาซะ จงทำให้การเปลี่ยนชื่อเป็นเรื่องง่าย และทำมันบ่อยๆ

ไม่อย่างนั้นคุณคงต้องไปยืนอธิบายให้น้องใหม่ในทีมฟังว่า getData จริงๆ แล้วมันใช้สำหรับเขียนข้อมูลลงไฟล์นะ... แถมคุณยังต้องทำหน้าตาเฉยแบบไม่รู้สึกอะไรด้วยล่ะ

หัวข้อที่เกี่ยวข้อง

  • Topic 3, ​_Software Entropy_​
  • Topic 40, ​_Refactoring_​
  • Topic 45, ​_The Requirements Pit_​

ความท้าทาย

  • เมื่อคุณเจอชื่อฟังก์ชันหรือเมธอดที่มันทั่วไปเกินไป ลองเปลี่ยนชื่อให้มันสื่อความหมายถึงสิ่งที่มัน "ทำจริงๆ" ดูครับ แล้วคุณจะพบว่ามันทำให้การ refactor ทำได้ง่ายขึ้นเยอะเลย

  • จากตัวอย่างที่เราเสนอไปให้ใช้ชื่อที่เฉพาะเจาะจงอย่าง buyer แทนชื่อดั้งเดิมที่เป็นทางการและกว้างเกินไปอย่าง user คุณล่ะมีชื่อไหนที่คุณใช้อยู่เป็นประจำแล้วคิดว่ามันน่าจะมีชื่อที่ดีกว่านี้บ้างไหม?

  • ชื่อต่างๆ ในระบบของคุณสอดคล้องกับคำศัพท์ที่ผู้ใช้งานในโดเมนนั้นๆ (domain) ใช้กันหรือเปล่า? ถ้าไม่ เพราะอะไรล่ะ? แล้วมันทำให้เกิดความลักลั่นในใจแบบ Stroop effect กับคนในทีมบ้างไหม?

  • ชื่อต่างๆ ในระบบของคุณเปลี่ยนยากหรือเปล่า? คุณพอจะทำอะไรได้บ้างเพื่อซ่อมหน้าต่างที่มันแตกบานนั้นให้กลับมาดีเหมือนเดิม?

ฟุตโน้ต (Footnotes)

[50]

คำเตือนจากผู้ที่เคยผ่านสมรภูมิมา: UTC มีไว้ให้ใช้ จงใช้มันซะ

[51]

https://en.wikipedia.org/wiki/Correlation_does_not_imply_causationion)

[52]

ลองดูที่ Topic 50 เรื่อง ​_Coconuts Don't Cut It_​

[53]

แต่เรื่องนี้ก็ระวังอย่าให้มันมากเกินไปนะ เราเคยรู้จักนักพัฒนาคนหนึ่งที่ชอบรื้อโค้ดของคนอื่นแล้วเขียนใหม่ทั้งหมด เพียงเพราะเขาอยากใช้กฎเกณฑ์การตั้งชื่อในแบบของเขาเอง

[54]

https://media-origin.pragprog.com/titles/tpp20/code/algorithm_speed/sort/src/main.rs)

[55]

ใช่แล้วครับ เราเคยแย้งเรื่องชื่อหนังสือไปแล้วเหมือนกัน

[56]

พบครั้งแรกใน _UML Distilled: A Brief Guide to the Standard Object Modeling Language_ [Fow00]

[57]

นี่เป็นคำแนะนำที่ดีมากโดยทั่วๆ ไป (ลองดู Topic 27, ​_Don't Outrun Your Headlights_​ นะครับ)

[58]

บางคนอาจจะแย้งว่า test-first และ test-driven development เป็นคนละเรื่องกัน โดยบอกว่าความตั้งใจของทั้งสองอย่างนั้นต่างกัน แต่ถ้าดูตามประวัติศาสตร์แล้ว test-first (ซึ่งมาจาก eXtreme Programming) ก็คือสิ่งเดียวกับที่เราเรียกกันว่า TDD ในตอนนี้นั่นแหละ

[59]

https://ronjeffries.com/categories/sudoku ต้องขอขอบคุณ Ron มากๆ ครับที่อนุญาตให้เราเอาเรื่องนี้มาเล่าต่อ

[60]

http://norvig.com/sudoku.html

[61]

เราพยายามเรื่องนี้กันมาตั้งแต่ปี 1986 เมื่อตอนที่ Cox และ Novobilski บัญญัติคำว่า “software IC” ไว้ในหนังสือ Objective-C ของพวกเขา เรื่อง Object-Oriented Programming _Object-Oriented Programming: An Evolutionary Approach_ [CN91]

[62]

ลองดู Topic 20 เรื่อง ​_Debugging_​ นะครับ

[63]

จำเพื่อนเก่าอย่าง Bobby Tables (https://xkcd.com/327) ได้ไหมครับ? ไหนๆ ก็พูดถึงแล้ว ลองเข้าไปดูที่ https://bobby-tables.com นะครับ ที่นั่นเขารวบรวมวิธีทำความสะอาดข้อมูล (sanitize data) ก่อนส่งไปคิวรี่ฐานข้อมูลไว้เพียบเลย

[64]

เทคนิคนี้ได้รับการพิสูจน์แล้วว่าประสบความสำเร็จในระดับชิป CPU ซึ่งการโจมตีที่เรารู้จักกันดีมักจะพุ่งเป้าไปที่ช่องโหว่ด้าน debugging และ administrative ถ้ามันหลุดขึ้นมาทีหนึ่ง ก็คือโดนยกเครื่องเลยล่ะครับ

[65]

NIST Special Publication 800-63B: Digital Identity Guidelines: Authentication and Lifecycle Management, available free online at https://doi.org/10.6028/NIST.SP.800-63b

[66]

นอกเสียจากว่าคุณจะจบปริญญาเอกด้านวิทยาการรหัสลับ (cryptography) และต่อให้เป็นอย่างนั้น คุณก็ต้องผ่านการรีวิวจากผู้เชี่ยวชาญ (peer review), การทดสอบในสนามจริงอย่างหนักหน่วงพร้อมการล่าเงินรางวัลจากบั๊ก (bug bounty) และมีงบประมาณสำหรับการบำรุงรักษาในระยะยาวด้วยนะ

[67]

_Studies of Interference in Serial Verbal Reactions_ [Str35]

[68]

เรามีรูปแผงนี้อยู่สองเวอร์ชันครับ เวอร์ชันหนึ่งใช้สีจริง ส่วนอีกเวอร์ชันเป็นระดับสีเทา (grayscale) ถ้าคุณเห็นภาพนี้เป็นสีขาวดำแล้วอยากได้เวอร์ชันสี หรือถ้าคุณมีปัญหาในการแยกแยะสีและอยากลองใช้เวอร์ชันสีเทา ลองแวะไปดูได้ที่ https://pragprog.com/the-pragmatic-programmer/stroop-effect นะครับ

[69]

รู้ไหมครับว่าทำไมเราถึงนิยมใช้ i เป็นตัวแปรวนลูป? คำตอบย้อนไปไกลถึง 60 ปีก่อนเลยล่ะ เพราะในภาษา FORTRAN ยุคแรกเริ่ม ตัวแปรที่ขึ้นต้นด้วยตัวอักษร I ถึง N จะถูกมองว่าเป็นจำนวนเต็ม (integers) เสมอ และ FORTRAN เองก็ได้รับอิทธิพลมาจากพีชคณิต (algebra) อีกทอดหนึ่งนั่นเอง

Copyright © 2020 Pearson Education, Inc.