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 ที่มีอยู่ในปัจจุบัน
3 Laws of TDD
Understanding the 3 rules of Test Driven Development is very fundamental in writing clean, stable and consistent code, one that leads to…

Five Rules of Clean Tests

การเขียน Unit test นั้น มันจะมีประโยชน์ และ มีประสิทธิภาพ สำหรับทีม programmers
แต่ unit test จำเป็นต้องมีคุณสมบัติ F.I.R.S.T ดังนี้

  • Fast (เร็ว)
  • Isolated (เป็นอิสระ)
  • Repeatable (ทำซ้ำได้)
  • Self-verify (validate ด้วยตัวเอง)
  • Timely (ใช้เวลาที่เหมาะสม)
Unit Tests ที่ดีต้องมีคุณสมบัติ FIRST