Clean Code: A Handbook of Agile Software Craftsmanship by Robert C. Martin เป็นหนังสือที่สอนเกี่ยวกับ best practices ต่างๆ ในการเขียน code ที่ดี
วันนี้เราจะมารู้จัก practices ต่างๆ ที่จะช่วยให้เราเขียน code ให้ง่ายต่อการทำความเข้าใจ และ ง่ายในการแก้ไข
Meaningful Name
ส่วนนี้จะพูดถึงกฏง่ายๆ ที่จะช่วยให้เราตั้งชื่อที่ดีเวลาที่เขียน code
Use Intention-Revealing Names
ชื่อของ variable, function, หรือ class ควรจะบ่งบอกว่า ทำไมมันถึงมีอยู่ บอกสิ่งที่มันทำและ วิธีการใช้งาน
ถ้าชื่อ variable, function, หรือ class ใดๆที่ต้องการ comment แล้ว นั้น คือ ชื่อที่ไม่ดี เพราะมันไม่ได้บ่งบอกถึงเจตนาของสิ่งที่มันทำ
Avoid Disinformation
programmers ต้องหลีกเลี่ยงการใช้ชื่อที่คลุมเครือใน code และ หลีกเลี่ยงการใช้ชื่อที่ควรหลีกเลี่ยง
เช่น ชื่อ hp, aix, และ sco เนื่องจากเป็นชื่อของระบบปฏิบัติการ Unix
หรือ
อีกตัวอย่างหนึ่งที่ควรเลี่ยง คือ ตั้งชื่อ กลุ่มของ accounts ว่า accountList ซึ่งจริงๆแล้วไม่ควรทำ เว้นแต่ว่ามันจะเป็น List จริงๆ
Make Meaningful Distinctions
ต้องสามารถตั้งชื่อ code ให้เป็นเอกเทศ เพื่อให้ทำงานกับ compiler หรือ interpreter ได้
เช่น วิธีการในการตั้งชื่อ variable ชื่อ klass เพราะว่าชื่อ class นั้นถูก reserved โดย compiler
แต่ในการเลี่ยงนั้น ก็ต้องระวังการตั้งชื่อที่เป็น number-series naming
เช่น a1, a2, ..., aN เพราะว่ามันไม่มีความหมายในตัวมันเอง
Use Pronounceable Name
อย่าตั้งชื่อที่ไม่สามารถอ่านออกเสียงได้
เช่น ตัวแปรชื่อ genymdhms ซึ่งย่อมาจาก generation year, month, day, hour, minute, และ second แต่ genymdhms มันไม่สามารถออกเสียงได้
เพราะฉะนั้น ควรหลีกเลี่ยงการตั้งชื่อในลักษณะนี้ด้วย
Use Searchable Names
ควรหลีกเลี่ยงด้วยการใช้ตัวอักษรเพียงตัวเดียวในการตั้งชื่อ เพราะมันอาจก่อให้เกิดปัญหาได้ เนื่องจากความยุ่งยากในการค้นหา
Avoid Encodings
หลีกเลี่ยงการ encoding ชื่อต่างๆ
เช่น การระบุ type ของ String variable โดยตั้งชื่อว่า phoneString
โดยเป็นการตั้งชื่อให้มีคำว่า String ร่วมด้วย เป็นการตั้งชื่อที่ไม่จำเป็น แถมมันยังสามารถสร้างความน่ารำคาญให้อีก
Interfaces and Implementations
การใช้ตัวอักษร I นำหน้า interface เช่น IShapeFactory ถ้าสามารถทำได้ให้เลี่ยงการใช้มัน
Class Names
ชื่อของ Classes และ Objects ควรเป็น “คำนาม” เช่น Customer, Book หรือ Student
หลีกเลี่ยงกลุ่มคำ เช่น Manager, Processor และ ควรหลีกเลี่ยงการใช้คำ “กริยา” ในการตั้งชื่อด้วย
Method Names
ตั้งชื่อ Method ด้วยคำ “กริยา” เช่น postPayment, deletePage, หรือ save
Don’t Be Cute
อย่าตั้งชื่อให้มันดูหลักแหลมดูฉลาดมากเกินไป ควรตั้งชื่อให้มีความชัดเจน ไม่ใช่ตั้งชื่อตามความสนุกสนานหรือเพราะว่ามันดูเท่
Pick One Word per Concept
มันจะน่าสับสนขนาดไหน ถ้าใช้คำว่า fetch, retrieve, และ get กับ methods ที่เหมือนกันแต่อยู่คนละ classes ควรเลือกใช้แบบใดแบบหนึ่งเท่านั้น
Classes
Classes Should Be Small!
ควรที่จะมีขนาดไม่ใหญ่จนเกินไป เราควรพยายามทำให้ class ไม่ม่ขนาดที่ใหญ่เกินไป และควรยึดขนาดของ class ที่เล็กเป็นกฏหลัก เวลาเราออกแบบ class
Encapsulation
variables และ utility functions ใน class ควรเป็น private หรือ เป็น protected ในกรณีที่ต้องการใช้เพื่อการ test ใน package เดียวกัน
The Single Responsibility Principle
Single Responsibility Principle (SRP) คือ หนึ่ง class หรือ module ที่ทำหน้าที่เพียงหนึ่งอย่าง ซึ่งหนึ่งอย่างนั้น ควรมีเหตุผลเพียงอย่างเดียวเท่านั้น ที่ class หรือ module นั้นๆ จะถูกแก้ไข
Functions
function เป็นกระบวนการหรือคำสั่งใน computer program ซึ่งสำคัญมากๆ เทคนิคต่อไปนี้จะช่วยให้เราเขียน function ได้ clean มากยิ่งขึ้น
Small
ความยาวของ code ต่อ line ไม่ควรยาวเกิน 150 characters (ส่วนตัวผมใช้ 120 characters ซึ่งเป็น default ของ IntelliJ)
Function ไม่ควรยาวเกิน 20 บรรทัด
Do One Thing
Function ต้องทำงานอย่างเดียวเท่านั้น ไม่ควรทำหลายอย่างใน function เดียวกัน
Use Descriptive Names
ตั้งชื่อให้สื่อความหมายกับการทำงานของ function ด้วย โดยใช้ชื่อที่ชัดเจนให้สื่อถึงการทำงานสิ่งเดียวสิ่งนั้นของมันเลย
Function Arguments
Function Arguments ไม่ควรเยอะเกิน 3 arguments ถ้า arguments มีจำนวนมาก ให้พิจารณาการ wrap arguments เหล่านั้นโดยใช้ class แล้วส่ง class นั้นเป็น argument แทน
อย่าใช้ flag arguments เช่น boolean เพราะมันบอกเป็นนัยว่า function นั้นๆกำลังทำงานมากกว่าหนึ่งอย่าง
Have No Side Effects
ตรวจสอบให้แน่ใจว่า function ต้องไม่มี side effects
เรื่อง side effects สำคัญเป็นอย่างมาก โดยเฉพาะอย่างยิ่งการเขียน code ในรูปแบบ functional programming
Command Query Separation
Function ควรจะดำเนินการอย่างใดอย่างหนึ่ง เช่น set result หรือ return result ไม่ใช่ทำทั้งสองอย่าง
Don’t Repeat Yourself
หลีกเลี่ยงการทำ duplicate code!
Comments
จริงๆแล้ว code ของเราควรอธิบายการทำงานของตัวมันเองได้ ไม่ใช่เข้าใจได้ด้วยการไปอ่าน comments
การตั้งชื่อที่ดี สามารถป้องกัน comments ได้
แต่สำหรับ Legal comments แล้ว จำเป็นต้องเขียน comment ด้วย ซึ่ง legal comment ในที่นี้ หมายถึง copyright และ licenses
Formatting
ควรตระหนัก และ เห็นถึงคุณค่าใน code formatting และใส่ใจในรายละเอียด และ ความเรียบร้อยของ code อยู่ตลอดเวลา
ควรตั้ง formatting rules ของทีม โดย formatting rules นี้ อาจจะพิจารณาการใช้ tools ต่างๆ มาช่วยในการทำงาน เพื่อให้การทำงานสะดวกและง่ายขึ้น
เช่น Linter, Prettier หรือ ใช้ความสามารถของ IDE ต่างๆ
แต่ทั้งนี้ทั้งนั้นแต่ละบริษัทหรือแต่ละ team อาจจะมีความแตกต่างกันเล็กน้อยใน formatting rules ดังนั้นทีมต้องตกลงและเข้าใจตรงกันใน formatting rules ที่จะใช้งานร่วมกันเป็นแบบไหน
Objects and Data Structures
ส่วนนี้เป็นเรื่องที่ค่อนข้างซับซ้อน ดังนั้นเราควรให้ความเอาใจใส่มันเยอะๆ หน่อย
Data Abstraction
ควรซ่อนส่วนของการ implementation หรือซ่อน data ไว้หลัง abstractions และ เผยเฉพาะ function ที่ทำงานกับข้อมูลนั้นๆ แทนที่เปิดเผยข้อมูลทั้งหมดออกมา
Data/Object Anti-Symmetry
ต้องเข้าใจความแตกต่างระหว่าง objects และ data structures
Objects: จะซ่อน data ไว้หลัง abstractions layer และ แสดง function ที่ทำงานกับ data นั้นๆ
Data Structures: แสดงข้อมูลของตนเพื่อความสะดวก และ ไม่มี meaningful function เหมือน objects
Error Handling
เทคนิค และ ข้อพิจารณาในการเขียน handles errors นั้นสำคัญ โดยเฉพาะ errors ที่เกิดจากปัจจัยภายนอก เราจึงควรพิจารณาเทคนิคในการจัดการ exceptions ให้เหมาะสม
Use Exceptions Rather Than Return Codes
ใช้ความสามารถของภาษาที่เราเขียนให้เกิดประโยชน์ ถ้าภาษารองรับการใช้งาน exceptions ให้ทำการ ใช้ exceptions ในการจัดการกับ errors แทนที่ return error codes
Write Your Try-Catch-Finally Statement First
Code ใน try blocks เหมือนกับ transactions เนื่องจาก catch block สามารถดักจับ program ใน state ที่สอดคล้องกัน
ใช้ finally Block ในการ clean up resources ในกรณีที่มีการใช้งาน resources แบบ connections
Use Unchecked Exceptions
ใช้ Unchecked Exceptions ทำหน้าที่เป็นข้อบกพร่องของ program
เช่น บางครั้ง มีการส่งค่าที่ไม่ถูกต้องให้ Method ของเรา ควรใช้ Unchecked Exceptions ในการจัดการกับเงื่อนไขประเภทนี้
Unchecked Exceptions ยังเป็น subclasses ของ Runtime Exception
โดยปกติแล้ว Unchecked runtime exceptions ทำหน้าที่เกี่ยวกับจัดการเงื่อนไขและการสะท้อนข้อผิดพลาดนั้นๆ
Don’t Return Null
อย่าทำการ return Null ให้ทำการ re-throwing exception หรือ return object พิเศษที่เราสร้างขึ้นมา ในการจัดการกับ exception แทน
การที่เรา return null จะทำให้เราไม่รู้สาเหตุของ error เลย
Unit Testing
มี programmers จำนวนที่ไม่น้อยที่ยังไม่เข้าใจความสำคัญในการเขียน unit tests และ ไม่เข้าใจจุดมุ่งหมายของการเขียน unit tests ที่ดี ว่าควรเป็นยังไง
Clean Tests
ปัจจัยที่ช่วยให้ tests ของเรา clean นั้น คือ มันจะต้องอ่านง่าย (Readability) code ของเราต้องมีความชัดเจน และ เรียบง่าย
The Three Laws of TDD
- ไม่เขียน production code จนกว่าจะเขียน test ทดสอบความผิดพลาดเสียก่อน
- ไม่เขียน unit test นอกเหนือจาก case ที่เพียงพอและคลุม fail case แล้ว (ไม่งั้นเราจะเขียน test โดยฟุ่มเฟือยและเกิด over thinking)
- ไม่เขียน production code เพิ่ม นอกจากมันจะผ่าน failing test ที่มีอยู่ในปัจจุบัน
Five Rules of Clean Tests
การเขียน Unit test นั้น มันจะมีประโยชน์ และ มีประสิทธิภาพ สำหรับทีม programmers
แต่ unit test จำเป็นต้องมีคุณสมบัติ F.I.R.S.T ดังนี้
- Fast (เร็ว)
- Isolated (เป็นอิสระ)
- Repeatable (ทำซ้ำได้)
- Self-verify (validate ด้วยตัวเอง)
- Timely (ใช้เวลาที่เหมาะสม)