# เขียน Dart อย่างมีสไตล์
#Dev#Dart#Flutterเขียนโค้ดในภาษา Dart/Flutter ยังไงไม่ให้โดนด่าบรรพบุรุษ
ซึ่งผมจะเอาข้อมูลมาจาก Effective Dart (opens new window) โดยเนื้อหาจะเป็นการให้เขียนโค้ดในภาษา Dart ไปในรูปแบบเดียวกันครับ

# เบื้องต้น
จะมีการแบ่งแต่ละส่วนออกเป็นส่วนย่อยๆ โดยมีคำดังต่อไปนี้
- ควร เป็นสิ่งควรจะทำตามเสมอ ไม่มีเหตุผลให้ไม่ทำตามนี้
- อย่า เป็นสิ่งที่ไม่ควรทำตาม
- แนะนำ ให้ทำตาม แต่จะไม่ทำตามก็ได้ในบางกรณี
- หลีกเลี่ยง ไม่ควรทำ เว้นเสียแต่ว่ามีเหตุให้ต้องทำ
- คิดก่อน อาจจะทำตาม หรือไม่ทำตามก็ได้แล้วแต่สถานการณ์
อีกส่วนที่สำคัญในการเขียนโค้ดที่ดีก็คือการมีรูปแบบที่ดี ไม่ว่าจะเป็นการตั้งชื่อ การจัดเรียง จะช่วยให้โค้ดดูเหมือนๆกัน ทำให้คนที่มาอ่านสามารถแก้ และเข้าใจโค้ดของเราได้มากยิ่งขึ้น
# การตั้งชื่อตัวแปรและอื่นๆ
มีสามรูปแบบ
- UpperCamelCase ขึ้นต้นแต่ละคำด้วยตัวพิมพ์ใหญ่
- lowerCamelCase ขึ้นต้นแต่ละคำด้วยตัวพิมพ์ใหญ่ ยกเว้นคำแรกสุดที่เป็นตัวพิมพ์เล็กทั้งหมด
- lowercase_with_underscores พิมพ์ด้วยตัวพิมพ์เล็กทั้งหมด โดยจะคั่นแต่ละคำด้วย _ (Underscore)
# ควร ตั้งชื่อ Type หรือชนิดด้วย UpperCamelCase
ตั้งชื่อ Class, enum, typedef, หรือชนิดต่างๆด้วยตัวพิมพ์ใหญ่ขึ้นต้นแต่ละคำโดยไม่มีอะไรคั่นแต่ละคำ
class SliderMenu { ... }
class HttpRequest { ... }
typedef Predicate<T> = bool Function(T value);
# ควร ตั้งชื่อ Library, Package, Folder, File ด้วย lowercase_with_underscores
ในบางระบบไฟล์การตั้งชื่อไฟล์เป็นตัวพิมพ์เล็กหรือใหญ่ให้ผลที่เหมือนกัน เพื่อหลีกเลี่ยงปัญหา เราจึงควรตั้งชื่อสิ่งเหล่านี้ด้วยตัวพิมพ์เล็กทั้งหมด แล้วคั่นแต่ละคำด้วย _
// ดี
library peg_parser.source_scanner;
import 'file_system.dart';
import 'slider_menu.dart';
// ไม่ดี
library pegparser.SourceScanner;
import 'file-system.dart';
import 'SliderMenu.dart';
# ควร ตั้งชื่อ Library ที่ import มาด้วย lowercase_with_underscores
// ดี
import 'dart:math' as math;
import 'package:angular_components/angular_components'
as angular_components;
import 'package:js/js.dart' as js;
// ไม่ดี
import 'dart:math' as Math;
import 'package:angular_components/angular_components'
as angularComponents;
import 'package:js/js.dart' as JS;
# ควร ตั้งชื่อตัวแปรทั่วๆไป หรือชื่อฟังก์ชันด้วย lowerCamelCase
var item;
HttpRequest httpRequest;
void align(bool clearItems) {
// ...
}
# แนะนำ ให้ใช้ lowerCamelCase สำหรับตัวแปรประเภท Constant ในโค้ดใหม่ๆ
// ดี
const pi = 3.14;
const defaultTimeout = 1000;
final urlScheme = RegExp('^([a-z]+):');
class Dice {
static final numberGenerator = Random();
}
// ไม่ดี
const PI = 3.14;
const DefaultTimeout = 1000;
final URL_SCHEME = RegExp('^([a-z]+):');
class Dice {
static final NUMBER_GENERATOR = Random();
}
ซึ่งอาจจะยังใช้การตั้งชื่อในรูปแบบ SCREAMING_CAPS ก็ได้ ถ้า
- เพิ่มโค้ดลงใน Library หรืดโค้ดเดิมๆที่ใช้ SCREAMING_CAPS
- ถ้ามีการ Generate โค้ดแล้วชื่อต้องสัมพันธ์กับโค้ดภาษาอื่น
Note
เหตุผลที่ไม่ใช้ SCREAMING_CAPS เพราะว่า
- SCREAMING_CAPS มันดูไม่สวยงามในบางกรณี
- ตัวแปรประเภท Constant ในหลายๆครั้งมักจะถูกเปลี่ยนเป็น final ทำให้ชื่อต้องถูกเปลี่ยนไปด้วย
- ตัวแปร values ที่ประกาศใน enum เป็น Constant และเป็นตัวพิมพ์เล็ก
# อย่า ใช้ _ นำหน้าชื่อตัวแปร หรือฟังก์ชันที่ไม่ได้ต้องการให้เป็น private
ในภาษา Dart เราจะใช้ _ ใน class ในการระบุความเป็น private
# อย่า ใช้ตัวย่อนำหน้าชื่อตัวแปร
เพราะว่า Autocomplete สามารถบ่งบอกชนิดของตัวแปรได้อยู่แล้วเราไม่ควรเขียน iNumber หรือ strName เป็นต้น
# การเรียงส่วนหัวของไฟล์
เพื่อที่จะให้ส่วนของการ import ไฟล์ต่างๆอ่านได้ง่าย เราจึงควรจะเรียงลำดับตามนี้
และแบ่งส่วนแต่ละส่วนคั่นด้วยบรรทัดว่าง
# ควร เรียงลำดับ dart: ขึ้นก่อนเป็นอันดับแรก
import 'dart:async';
import 'dart:html';
import 'package:bar/bar.dart';
import 'package:foo/foo.dart';
# ควร เรียงลำดับ package: ขึ้นก่อนการ import แบบ relative
import 'package:bar/bar.dart';
import 'package:foo/foo.dart';
import 'util.dart';
# แนะนำ ให้เรียงลำดับ package: ที่มาจากภายนอกก่อน
import 'package:bar/bar.dart';
import 'package:foo/foo.dart';
import 'package:my_package/util.dart';
# ควร เอา export ไว้ท้ายสุดหลังจาก import ทุกอัน
import 'src/error.dart';
import 'src/foo_bar.dart';
export 'src/error.dart';
# ควร เรียงลำดับในแต่ละส่วนตามลำดับตัวอักษร
import 'package:bar/bar.dart';
import 'package:foo/foo.dart';
import 'foo.dart';
import 'foo/foo.dart';
# การจัดรูปแบบโค้ด
เหมือนกับภาษาอื่นๆ Dart ไม่ได้สนใจบรรทัดว่าง หรือช่องว่าง แต่ว่าคนเขียนโปรแกรมก็ยังเห็นอยู่
การใส่การเว้นบรรทัด หรือเว้นวรรคจะช่วยให้โค้ดเราอ่านได้ง่ายขึ้น
# ควร ใช้ dartfmt ในการจัดโค้ด
dartfmt เป็นตัวช่วยในการจัดโค้ดให้เราได้อย่างสะดวกสบายมากๆครับ แต่ก็จะมีบางกรณีที่มันก็ช่วยเราไม่ได้เหมือนกัน เราจึงต้องช่วยตัวเองกันไป ถ้าใช้ VSCode สามารถใช้ settings ด้านล่างได้ครับ
{
...
"[dart]": {
"editor.tabSize": 2,
"editor.insertSpaces": true,
"editor.detectIndentation": false,
"editor.formatOnSave": true,
"editor.rulers": [80],
}
...
}
# คิดก่อน ให้ dartfmt แก้โค้ด
dartfmt ช่วยให้โค้ดเราสวยขึ้นได้จริง แต่ถ้าช่วยไม่ได้ บางทีเราอาจจะต้องเปลี่ยนวิธีการเขียน เช่น โค้ดบางอย่างซับซ้อนเกินไป มีการใช้ Operator ยุ่งยากไม่เหมาะสม
# หลีกเลี่ยง การเขียนโค้ดแต่ละบรรทัดยาวเกิน 80 ตัวอักษร
การที่โค้ดแต่ละบรรทัดยาวเป็นพรืดๆ บางครั้งมันก็ทำให้โค้ดอ่านยากขึ้นเช่นกัน
ในบางครั้งเราตั้งชื่อตัวแปรยาวเกินไปก็อาจจะต้องคิดก่อนว่าเราตั้งชื่อยาวเกินความจำเป็นหรือไม่ สามารถลดคำลงได้หรือไม่
ยกเว้น URL หรือ ชื่อไฟล์ เราสามารถปล่อยให้ยาวเกิน 80 ตัวได้ เพื่อให้ง่ายต่อการค้นหา ยกเว้น String ที่มีหลายบรรทัด
# ควร ใส่ปีกกา {} เสมอๆในการทำงานประเภท if-else, for, while, อื่นๆ
เพื่อหลีกเลี่ยงปัญหาบางอย่าง เช่น การจับคู่ if-else ผิดพลาด
if (isWeekDay) {
print('Bike to work!');
} else {
print('Go dancing or read a book!');
}
ยกเว้น if ที่ไม่มี else และมีความยาวโค้ดไม่มากนัก
if (arg == null) return defaultValue;
แต่ถ้ายาวก็ขึ้นบรรทัดใหม่แล้วปิดด้วย {}
if (overflowChars != other.overflowChars) {
return overflowChars < other.overflowChars;
}