web analytics

การเขียนภาษา Dart ที่มีประสิทธิภาพ

สวัสดีครับ ทุกท่าน วันสองวันนี้เป็นช่วงเวลาว่างๆ เลยหาอะไรมาอ่านเล่น ผมไปเจอกับบทความใน guides ของเว็บไซต์ dart.dev ที่มีเนื้อหาเกี่ยวกับแนวทางการเขียน Dart อ่านแล้วก็น่าสนใจดี เพราะช่วงแรกๆของผมนั้น ผมชอบเขียนปะปนกับ Java style ใน dart เสมอๆ เลยนำมาสรุปไว้ในบล็อกนี้ โดยผมเลือกมาเฉพาะส่วนหลักๆที่ผมคิดว่าสำคัญกับการทำงานร่วมกันกับคนอื่นๆ

โดยขอย้ำว่า นี่เป็นเพียงแนวทางเท่านั้นนะ ไม่จำเป็นต้องทำตามทั้งหมดก็ได้ สุดท้ายแล้วก็อยู่ที่การตกลงกันภายในทีม

สามารถอ่านฉบับเต็มได้ที่นี่เลย
https://dart.dev/guides/language/effective-dart

ไม่ใช้ “new” keyword

ตั้งแต่ dart2 คำว่า “new” ที่ใช้เวลาเราสร้าง instance นั้น ได้กำหนดเป็น optional สิ่งนี้ช่วยลด code ที่ซ้ำๆไปได้เยอะเลย ดังนั้นเลิกใช้มันได้เลย

บรรทัดละ 80 ตัวอักษร

Dart บอกว่า อ้างอิงจากผลการศึกษาที่ว่าสายตาคนเราจะกวาดตาอ่านได้ดี คือราวๆ 80 ตัวอักษรในหนึ่งบรรทัด นั่นเป็นเหตุผลว่าทำไมหนังสือพิมพ์ถึงมีหลายๆคอลัมน์

ตัวอย่าง code ที่มี 80 ตัวอักษร

สำหรับใน Android Studio สามารถปรับค่าของการขึ้นบรรทัดใหม่อัตโนมัติ หรือเรียกว่า Hard Wrap ได้ที่ Setting และค้นหาคำว่า “hard wrap” ปรับได้ตามใจเลย

Classes , Enum , Type , Extension

ชื่อ Class จะใช้การตั้งชื่อแบบ UpperCamelCase รวมถึง enum , typedef ด้วย

ชื่อไฟล์ Dart และ Package

ชื่อไฟล์ dart , directory, library, ชื่อ package ต่างๆ จะใช้การตั้งชื่อแบบ lowercase_with_underscores

Import prefixes

การตั้งชื่อให้กับ package ที่ import เข้ามา จะใช้ keyword “as” โดย prefix ที่ว่านี้ แนะนำให้ตั้งชื่อแบบ lowercase_with_underscores

ลำดับของ Import

Dart แนะนำให้จัดลำดับการ import จะแบ่งเป็น 4 กลุ่ม เรียงตามลำดับคือ
1. กลุ่ม dart หรือ Flutter ที่เป็น core package
2. กลุ่ม package แบบชื่อเต็ม ที่เป็น library
3. กลุ่ม package แบบ relative path ในโปรเจคของเรา
4. กลุ่ม export สำหรับทำเป็น external package

จากนั้นก็เรียงตามลำดับตัวอักษรในแต่ละกลุ่ม

สำหรับไฟล์ dart ภายในโปรเจคของเรา จะสามารถ import แบบ package: หรือ relative path ก็ได้ ซึ่ง Dart แนะนำให้ใช้แบบ relative path

เช่น

my_package
└─ lib
├─ src
│ └─ utils.dart
└─ api.dart

แต่ใน Android Studio หลายๆครั้ง มักจะแนะนำให้ใช้แบบ package: ซะอย่างนั้น แม้จะเป็น dart ในโปรเจคของเราเองก็ตาม

ตัวแปรต่างๆ (Variable)

ตัวแปรไม่ว่าจะเป็น members ของ Class หรือที่อยู่ top-level รวมทั้งเป็น parameters หรือ named parameters จะใช้การตั้งชื่อแบบ lowerCamelCase

การตั้งชื่อตัวแปรที่ไม่ใช่ประเภท boolean แนะนำให้ใช้คำนาม และหลีกเลี่ยงการใช้คำที่มาจากตัวย่อ

ค่าคงที่ (Constant)

ค่าคงที่ต่างๆจะใช้ keyword ว่า “const” หรือ “final” และจะใช้การตั้งชื่อแบบ lowerCamelCase

คำย่อ (acronyms)

การตั้งชื่อ Class ที่มาจากคำเต็มๆแล้วต่อย่อให้สั้น จะแบ่งเป็น 2 กรณี คือ กรณีที่ย่อแล้วมี 2 ตัวอักษร และกรณีที่ยาวกว่า 2 ตัวอักษร

2 ตัวอักษร

เช่น
input/output = IO
User Interface = UI

การตั้งชื่อ Class จะใช้ตัวใหญ่ทั้งหมด (Fully Capitalized) และใช้ lowerCamelCase สำหรับชื่อตัวแปร

มากกว่า 2 ตัวอักษร

เช่น
HTTP = Hypertext Transfer Protocol

ใช้การตั้งชื่อขึ้นต้นตัวใหญ่ตามด้วยตัวเล็ก (Capitalized)

ตัวย่อ (abbreviations)

ตัวย่อคือการตัดเอาตัวอักษรบางส่วนของคำมาใช้เรียกสั้นๆ

เช่น
id = identifier

ชื่อ Class จะใช้ Capitailzed เช่น Id
ส่วนชื่อตัวแปรจะใช้ lowerCamelCase

Method

method โดยทั่วไปแนะนำให้ตั้งชื่อที่เมื่ออ่านแล้วมีลักษณะเป็นประโยค จะทำให้สามารถเข้าใจได้ง่าย

หากมี return type เป็น boolean ให้ใช้ชื่อเป็นคำคุณศัพท์หรือคำกริยา

แนะนำว่าให้ใช้ชื่อที่เป็นไปในทางบวก
เช่น isConnected แทน isNotConnected

หาก method นั้น ทำงานลักษณะ copy ไปเป็น state ใหม่ให้ใช้คำว่า to ขึ้นต้น

หาก method นั้น ทำงานลักษณะเปลี่ยน object เป็น type ที่ต่างออกไป ให้ใช้คำว่า as ขึ้นต้น

แล้วก็ในชื่อ method ไม่จำเป็นต้องอธิบาย argument ว่าทำอะไร เพราะยังไง ก็สามารถดูจาก definetion ได้ และอาจจะยิ่งทำให้สับสน

Callback parameters ที่ไม่ใช้

หลายครั้งจะมี callback ที่มี parameter แต่เราไม่ได้ใช้ ให้ตั้งชื่อมันด้วย _ (underscore) หรือ ถ้ามีหลาย parameter จะใช้ __ , ___ ไปเรื่อยๆ

Prefix letters

การตั้งชื่อตัวแปรโดยใช้ตัวอักษรนำหน้าเพื่อระบุประเภทอะไรบางอย่าง ใน dart จะไม่แนะนำให้ใช้

แต่ว่าใน Flutter Repository นั้นจะมีใช้ prefix แบบนี้บ้าง เช่น จะใช้ “k” เป็น prefix สำหรับ ค่าคงที่ที่เป็น global 

เรื่องของ space

เว้น space ระหว่าง operator เพื่อให้อ่านง่าย

เว้น space หลัง , และ : เสมอ

การเขียน for loop ต้องเว้น space แบบนี้

และให้เว้น space ก่อน { ของ method body

การเขียน if

อย่างที่รู้กันว่า if-else มี style เขียนหลายแบบ เคยเป็นที่ถกเถียงกันในกลุ่มโปรแกรมเมอร์กันด้วย ว่า style ไหนคือดีที่สุด ส่วนใน dart แนะนำว่าให้ใช้แบบนี้

กรณีไม่มี else จะเขียนแบบบรรทัดเดียว แบบที่ไม่มี { } ก็ได้ แต่ถ้าขึ้นบรรทัดใหม่ให้ใช้ { } เสมอ

Switch case

การเขียน switch case จะมี indent และเว้นบรรทัดระหว่าง case ทุกครั้ง

Constructor

ส่วนกำหนดค่าเริ่มต้นใน constructor ให้เขียนแยกบรรทัดในแต่ละ field

Comments

comment ที่เราเห็นกันบ่อยๆจะมี 3 แบบ คือ
//
/* */
///

// คือ comment โดยทั่วไปจะใช้เพื่ออธิบายเพิ่มเติมว่ากำลังทำอะไร

ส่วน /* */ หรือเรียกว่า Block comment จะใช้สำหรับ comment code ส่วนที่ไม่ใช้

และ comment /// จะใช้สำหรับเป็นคำอธิบายแบบทางการ สามารถนำไป generate เป็น document ได้

อย่างไรก็ตาม การ comment แบบ /// นั้นมาจาก C# แต่ว่าใน dart ยังรองรับ /** */ ของ Java โดยทั้งสองแบบเป็น comment doc สามารถใช้ได้ทั้งคู่ แต่ dart แนะนำแบบ C# มากกว่า เพราะอ่านง่ายกว่า

forEach

แนะนำให้ใช้ for (var .. in ..) สำหรับคำสั่งหลาย statement ส่วน .foreach() จะใช้สำหรับกำหนดชื่อ method ลงไปเป็น parameter

ไม่ต้องเขียน type ถ้าไม่จำเป็น

.map(T) เป็น method ที่น่าจะใช้กันบ่อยๆ โดย Dart แนะนำว่าไม่ต้องเขียนชื่อ type ใน parameter ของ map()

type arguments ของ Generic ก็แนะนำว่าเขียน type เพียงจุดเดียวก็พอ

ใช้ => สำหรับ method ง่ายๆ

method ไหนที่ทำงานง่ายๆ หรือเป็น getter ถ้าเป็นไปได้ให้ใช้ => expression

Dart Analysis !

ทั้งหมดที่ว่าไปนี้ มีบางส่วนอยู่ใน Android studio เรียบร้อยแล้ว เช่น naming ตัวแปร โดยมันติดมากับ dart plugin นั่นเอง ทำให้เวลาเราเขียน Flutter จะมีหน้าต่าง Dart Analysis คอยเช็คให้ พร้อมกับแสดง warning

เราสามารถไป ignore ได้

แต่ถ้าไม่สนใจจะเขียนตาม Guide นี้ ก็ยังสามารถ compile ได้ตามปกติ อย่างผมเองเขียนไปเขียนมา ใน dart analysis ก็มี warning 400 กว่าจุดเลย (แฮะๆ)

หวังว่าบทความจะมีประโยชน์กับผู้อ่านครับ (:

Reference
https://dart.dev/guides/language/effective-dart