Dart คือ ภาษาโปรแกรมที่พัฒนาโดย Google ถูกนำมาใช้สำหรับสร้างโปรแกรมหรือ Apps
ต่างๆ ไม่ว่าจะเป็นโปรแกรมหรือ Apps สำหรับมือถือ หรือ โปรแกรมสำหรับ Desktop หรือแม้แต่
แปลงเป็น JavaScript สำหรับใช้งานในเว็บไซต์หรือ Web Apps เป็นต้น
เนื้อหาเกี่ยวกับ Dart เราจะยังไม่เจาะจงไปที่ platforms แต่จะทำความเข้าใจเบื้องต้น เกี่ยวกับ
รูปแบบของภาษา หรือโครงสร้างของภาษาโปรแกรม และการใช้งานเบื้องต้น ดังนั้น เราจะไม่ลงไปที่
การติดตั้งตัว Dart SDK แต่จะใช้การทำงานผ่านเว็บไซต์แทน
เราจะทดสอบโค้ด และรันคำสั่งภาษาโปรแกรม Dart ผ่านเว็บไซต์ ตามลิ้งค์ด้านล่าง
เริ่มต้น Dart Programming
ในทุก App ของโปรแกรม ภาษา Dart จะมีฟังก์ชั่น main() เสมอ เราสามารถทดสอบรูปแบบคำสั่งเริ่มต้น
โดยการแสดงข้อความคำว่า "Hello World" โดยใช้ฟังกืชั่น print() ดังนี้
void main() { // void ไม่คืนค่าใดๆ ทำงานใน main() ฟังก์ชั่น print('Hello, World!'); // แสดงข้อความ Hello, World! }
คล้ายๆ กับรูปแบบการทดสอบของโปรแกรมภาษาใดๆ ก็คือ เราต้องการดูว่ามันทำงานได้หรือไม่ โดยการให้ทำการแสดง
ข้อความที่ต้องการ
ตัวแปร Variables
ต่อไปเป็นส่วนของตัวแปรเบื้องต้น สมมติเราต้องการแทนข้อความคำว่า 'Hello, World!' ด้วยตัวแปร ก็จะได้เป็น
void main() { var mytext = 'Hello, World! use variable'; print(mytext); }
เราพอจะเห็นคร่าวแล้วว่า จะคล้ายๆ กับ JavaScript เราสามารถกำหนดตัวแปร ให้เป็นค่าต่างๆ ไม่ว่าจะเป็นตัวเลข ข้อความ
เลขทศนิยม Array (ใน Dart จะเรียกว่า List) Object (ใน Dart จะเรียก Sets หรือ Maps) เหล่านี้เป็นต้น ลองดูตัวอย่าง
void main() { var name = 'Ebiwayo'; var age = 24; var hobby = ['Reading', 'Jogging', 'Shopping']; var address = { 'city': ['Bangkok'], 'country': 'Thailand' }; print(name.runtimeType); // String print(age.runtimeType); // int print(hobby.runtimeType); // List<String> print(address.runtimeType); // Map<String, Object> }
ข้างต้นเราลองกำหนดตัวแปร แล้วเรียกใช้งานโดยให้แสดง ชนิดของตัวแปร ในภาษา Dart ซึ่งจะได้ค่า ตามที่แสดงด้านหลัง
การควบคุมชุดคำสั่ง Control flow statements
ในการควบคุมขั้นตอนการทำงานของโปรแกรม Dart ใช้รูปแบบชุดคำสั่งในการกำหนดรูปแบบการควบคุม
เหมือนกับในหลายๆ ภาษา ซึ่งเป็นรูปแบบปกติทั่วไป ไม่ว่าจะเป็นการใช้งาน if else, for loops,while / do-while loops,
break / continue, switch case เหล่านี้เป็นต้น
void main() { var name = 'Ebiwayo'; var age = 24; var hobby = ['Reading', 'Jogging', 'Shopping']; for(var i = 0; i < hobby.length; i++){ print(hobby[i]); } if(age>18){ print(name +' น่าจะเรียนจบ ม.ปลายแล้ว'); } }
ฟังก์ชั่น Functions
สำหรับการใช้งานฟังก์ชั่นใน Dart เราควรกำหนดชนิดของข้อมูลที่ต้องมีการคืนค่าจากฟังก์ชั่น และควรกำหนด
ประเภทของ arguments ที่นำไปใช้ในฟังพ์ชั้นด้วย
void main() { var name = 'Ebiwayo'; var age = 24; // สร้างฟังก์ชั่นหาปี พ.ศ. เกิดจาก อายุ int yearBorned(int age){ var now = new DateTime.now(); return (now.year-age)+543; } // เรียกใช้งานฟังก์ชั่น var borned = yearBorned(age); print(name +' เกิดปี พ.ศ. '+ borned.toString()); // Ebiwayo เกิดปี พ.ศ. 2538 }
การ Comments
สำหรับการ comment หรือปิดการทำงานของโค้ดบางส่วน จะใช้ // ไว้หน้าบรรทัดที่ต้องการปิดการทำงาน
หรือ comments ไว้ หรือจะใช้ /* เปิด และปิด */ สำหรับการ comments หลายๆ บรรทัดได้
void main() { var name = 'Ebiwayo'; var age = 24; // var hobby = ['Reading', 'Jogging', 'Shopping']; /*var address = { 'city': ['Bangkok'], 'country': 'Thailand' }; */ // สร้างฟังก์ชั่นหาปี พ.ศ. เกิดจาก อายุ int yearBorned(int age){ var now = new DateTime.now(); return (now.year-age)+543; } // เรียกใช้งานฟังก์ชั่น var borned = yearBorned(age); print(name +' เกิดปี พ.ศ. '+ borned.toString()); // Ebiwayo เกิดปี พ.ศ. 2538 }
การใช้งาน imports
ใน Dart เราสามารถเรียกใช้งานชุดคำสั่งจาก library หลักของ Dart เอง หรือจาก library ภายนอก รวมถึงการ
เรียกใช้จากไฟล์ได้โดยการใช้งานคำสั่ง import
// เรียกใช้งาน core libraries import 'dart:math'; // import 'dart:math' as math; // การ import พร้อมกับหนดชื่ออ้างอิง // เรียกใช้งาน libraries จาก package ภายนอก // import 'package:test/test.dart'; // เรียกใช้งาน จาก files // import 'path/to/my_other_file.dart'; void main() { print(pi); // แสดงค่า PI ที่อยู่ใน math core package library ค่าประมาณ 3.141592653589793 // เราสามารถใช้คำสั่ง import พร้อมกำหนดเชื่อเรียกอ้างอิงได้ เช่น // import 'dart:math' as math; // เวลาใช้งาน ก็จะเรียกเป็น print(math.pi); }
ตัวแปร pi เป็นค่าคงที่ของ core package library สำหรับการจัดการทางคณิตศาสตร์
การใช้งาน Classes
การกำหนด class เป็นรูปแบบการเขียนโปรแกรมในเชิงวัตถุ (OOP: Object-oriented programming)
เป็นเหมือนการกำหนดพิมพ์เขียวในการสร้าง Object ใดๆ ขึ้นมาใช้งาน โดยมีรูปแบบและโครงสร้างเฉพาะตัว
อาจจะมีการกำหนดสถานะค่าเริ่มต้น ของตัวแปรหรือ attribute มีรูปคำสั่งหรือฟังก์ชั่นการทำงาน ดูตัวอย่าง
class เบื้องต้นในโค้ดด้านล่าง เป็น class สำหรับการหาพื้นที่สีเหลี่ยม
import 'dart:math'; // สร้าง class ชื่อ Rectangle class Rectangle{ double width; double height; // กำหนด constructor ชื่อเดียวกับ class แบบมี parameter 2 ค่า Rectangle(this.width, this.height){ // กำหนดโค้ด หรือการเรียกใช้คำสั่งเริ่มต้น } // หรือกำหนด constructor แบบไม่มีการกำหนดโค้ดริ่มต้น ลักษณะนี้แทนได้ // Rectangle(this.width, this.height); // กำหนดฟังก์ชั่น หรือ method ในที่นี้เรากำหนด การหาพื้นที่สี่เหลี่ยม double area(){ return width * height; } } void main() { // ทดสอบใช้งาน Rectangle class โดยกำหนดความกว้าง และความสูงเท่ากับ 100 // จากนั้นเรียกใช้งาน method ชื่อว่า area() จะคืนค่าพื้นที่ของสี่เหลี่ยมกลับมา print(Rectangle(100,100).area()); // 10000 }
การใช้งาน Interfaces and abstract classes
ในภาษา Dart จะไม่มี keyword ที่ชื่อ interface ซึ่ง interface ก็จะคล้ายๆ กับรูปแบบการกำหนด property ของ object
โดยในภาษา Dart ก็จะหมายถึงการกำหนดตัวแปรสมาชิก กับ method หรือฟังก์ชั่นภายใน class
สำหรับการใช้งาน interface ในภาษา Dart เราสามารถ กำหนดในรูปแบบของ class ปกติทั่วไป ทั้งนี้ก็เพราะ ใน Dart จะมองว่า
ทุก class จะเป็น interface ในตัวของมันเองอยู่แล้วโดยนัย ซึ่งเราสามาถ สร้าง class โดย implement interface ใน class นั้นๆ ได้
ตัวอย่างเช่น
import 'dart:math'; // สร้าง class ชื่อ Rectangle class Rectangle{ double width; double height; // กำหนด constructor ชื่อเดียวกับ class แบบมี parameter 2 ค่า Rectangle(this.width, this.height){ // กำหนดโค้ด หรือการเรียกใช้คำสั่งเริ่มต้น } double area(){ return width * height; } } class Circle implements Rectangle{ double width; double height; double radius; Circle(this.radius); double area(){ return pi*pow(radius,2); } } void main() { print(Circle(7).area()); // 153.93804002589985 }
ในการใช้งาน abstract class เราสามารถกำหนดโดยเติมคำว่า abstract ไว้หน้า class ที่ต้องการ โดยการใช้งาน abstract ก็เพื่อใช้สำหรับ
กำหนดเป็น interface โดยตรง
import 'dart:math'; // สร้าง abstract class abstract class Shape{ void area(); // มี area method หาพื้นที่ void perimeter(); // มี perieter method หาความยาวเส้นขอบ } // สร้าง class ชื่อ Rectangle imnplement จาก Shape class Rectangle implements Shape{ double width; double height; Rectangle(this.width, this.height); double area(){ return width * height; } double perimeter(){ return (width * 2) + (height * 2); } } // สร้าง class ชื่อ Circle imnplement จาก Shape class Circle implements Shape{ double radius; Circle(this.radius); double area(){ return pi * pow(radius, 2); } double perimeter(){ return 2 * pi * radius; } } void main() { print(Rectangle(100,200).area()); // 20000 print(Rectangle(100,200).perimeter()); // 600 print(Circle(7).area()); // 153.93804002589985 print(Circle(7).perimeter()); // 43.982297150257104 }
การสืบทอด Inheritance
สำหรับการสืบทอดโดยการใช้การ extends เป็นการสร้าง class ย่อย จาก class หลักอีกทีหนึ่ง ทำให้เราสามารถ
ใช้ค่าของตัวแปร และ method ใน class ย่อยที่สร้างขึ้นมาได้ โดยอ้างอิงค่าผ่านการใช้คำสั่ง super
class Tv{ void turnOn() { _illuminateDisplay(); _activateIrSensor(); } void _illuminateDisplay(){ print("Illuminate Display"); } void _activateIrSensor(){ print("Activate IR Sensor"); } } // สืบทอด class ย่อยด้วยการ extends จาก parent class class SmartTv extends Tv{ void turnOn() { super.turnOn(); // เรียกใช้ method ของ parent class _bootNetworkInterface(); _initializeMemory(); _upgradeApps(); } void _bootNetworkInterface(){ print("Boot Network Interface"); } void _initializeMemory(){ print("Initialize Memory"); } void _upgradeApps(){ print("Upgrade Apps"); } } void main() { Tv().turnOn(); // ทีวีปกติ เปิดใช้งาน SmartTv().turnOn(); // สมาร์ททีวี เปิดใช้งาน // ผลลัพธ์ที่ได้ ลำดับการทำงานของ Tv และ SmartTv // ส่วนของ Tv // Illuminate Display // Activate IR Sensor // ส่วนของ SmartTv // Illuminate Display // Activate IR Sensor // Boot Network Interface // Initialize Memory // Upgrade Apps }
การใช้งาน Mixins
Mixins เป็นวิธีที่ใช้ในการเรียกใช้ class ซ้ำหรือนำมาใช้งานอีกครั้ง ในกรณีที่มีการสืบทอด class หลายลำดับขั้น
โดย class ที่จะนำมาใช้งานเป็น mixins นั้นต้องเป็น class ที่ไม่ได้สืบทอดหรือ extends มาจาก class อื่น นอกจาก Object class
รวมถึงเป็น class ที่ไม่ได้กำหนด contructor parameter และไม่มีการเรียกใช้คำสั่ง super() ด้วย
ยกตัวอย่าง โครงสร้าง Animal class แยกเป็น Mammal (สัตว์เลี้ยงลูกด้วยนม) และ Bird (สัตวปีก)
แล้วก็แยกย่อย ในแต่ละประเภทอีก ดังนี้
Animal - Mammal - Dolphin - Bat - Cat - Bird - Dove - Duck
mixins class ก็จะเหมือน class ของการกระทำหรือพฤติกรรม ที่สามารถนำมาใช้งานซ้ำได้ เช่น ปลาโลมา อยู่ในส่วนของ
สัตว์เลี้ยงลูกด้วยนมสามารถว่ายน้ำได้ เช่นเดียวกับเป็ดที่อยู่ในกลุ่มสัตว์ปีก ค้างคาวสามารถบินได้เหมือนสัตว์ปีก ค้างคาว แมว
นกพิราบ และเป็ด สามารถเดินได้ เหล่านี้เป็นต้น พฤติกรรมอย่าง การว่ายน้ำได้ เดินได้ และบินได้ เป็นส่วนที่ mixins ใช้ประโยชน์
ในการนำไปใช้ซ้ำใน class ดูตัวอย่างประกอบ
// โครงสร้าง class ที่มีการสืบทอดกันในหลายลำดับขั้น class Animal{} class Mammal extends Animal{} class Bird extends Animal{} // mixins class ที่สามารถนำไปใช้ร่วมกัน หรือเรียกใช้ซ้ำได้ class Walker{ void walk(){ print("I am walking"); } } class Swimmer{ void swim(){ print("i am swimming"); } } class Flyer{ void fly(){ print("i am flying"); } } // การใช้งาน mixins class เรียกใช้ผ่าน with keyword class Dilphin extends Mammal with Swimmer{} class Bat extends Mammal with Walker, Flyer{} class Cat extends Mammal with Walker{} class Dove extends Bird with Walker, Flyer{} class Duck extends Bird with Walker, Swimmer, Flyer{} void main() { Cat cat = Cat(); Dove dove = Dove(); // A cat can walk cat.walk(); // ผลลัพธ์ // I am walking // a dove can walk and fly dove.walk(); dove.fly(); // ผลลัพธ์ // I am walking // I am flying }
การใช้งาน Async
การดำเนินการในรูปแบบ Asynchronous ช่วยให้การทำงานของโปรแกรมเสร็จสมบูรณ์ได้ขณะที่กำลังรอการดำเนินการอื่น
ให้ทำงานเสร็จสิ้นอยู่ ยกตัวอย่างการดำเนินการในรูปแบบ asynchronous เช่น การเรียกดูข้อมูลผ่านเครือข่ายเน็ตเวิร์ค
การบันทึกข้อมูลลงฐานข้อมูล การอ่านข้อมูลจากไฟล์ เหล่านี้เป็นต้น
สำหรับการใช้งาน asynchronous ใน Dart เราสามารถใช้ Future class ในการดำเนินการ ร่วมกับคีร์เวิร์ด async และ await
มาดูตัวอย่างเล็กน้อย ของรูปแบบการทำงานแบบ asynchronous ที่ไม่ถูกต้อง
// ฟังก์ชั่น่สำหรับจำลองแสดงข้อมูลการสั่งซื้อ String createOrderMessage () { var order = getUserOrder(); // เรียกข้อมูลการสั่งซื้อจากฟังก์ชั่น getUserOrder() อีกที return 'Your order is: $order'; // คืนค่าข้อมูลการสั่งซื้อ } // ฟังก์ชั่นจำลอง ข้อมูลการสั่งซื้อ Future<String> getUserOrder() { // จำลอง และสมมติว่า การทำงานส่่วนนี้มีความซับซ้อน และทำงานช้า // โดยให้ดีเลย์การคืนค่ากลับในอีก 4 วินาที return Future.delayed(Duration(seconds: 4), () => 'Large Latte'); } void main () { // เมื่อมีการเรียกใช้ฟังก์ชั่น createOrderMessage() print(createOrderMessage()); // ผลลัพธ์ที่ได้ จะเป็น แสดงข้อความว่า // Your order is: Instance of '_Future<String>' }
จะเห็นเมื่อเราเรียกฟังก์ชั่น createOrderMessage() ตัวแปร order จะมีการกำหนดค่าให้มีค่าเท่ากับ ค่าที่ได้จากการเรียก
ใช้งานฟังก์ชั่น getUserOrder() อีกที และค่าที่คืนมาจากฟังก์ชั่นนี้ จะใช้เวลา 4 วินาที ถึงจะคืนค่ากลับมา เป็นลักษณะของการ
ทำงานแบบ asynchronous แต่เนื่องจาก เราไม่ได้กำหนดรูปแบบของข้อมูล ว่าเป็นแบบ asynchronous ทำให้ในบรรทัดต่อมา
มีการคืนค่าในทันที โดยที่ยังไม่ถึง 4 วินาที จากคำสั่ง
return 'Your order is: $order'; // คืนค่าข้อมูลการสั่งซื้อ
ทำให้ผลลัพธ์ของต้วแปร order ซึ่งเราใช้ $order สำหรับแสดงในรูปแบบข้อความ มีค่าเป็น
// Your order is: Instance of '_Future<String>'
แทนที่จะเป็น // Your order is: Large Latte
ดังนั้น เราควรเรียกใช้งานการดำเนินการแบบ asynchronous ให้ถูกต้อง โดยใช้งาน sync และ await ประกอบ เป็นดังนี้
// ฟังก์ชั่น่สำหรับจำลองแสดงข้อมูลการสั่งซื้อ รองรับ Asynchronous Future<String> createOrderMessage() async { var order = await getUserOrder(); // เรียกข้อมูลการสั่งซื้อจากฟังก์ชั่น getUserOrder() // ค่าจากตัวแปร order ให้รอข้อมูล จากฟังก์ชั่น getUserOrder() ก่อนนำไปใช้งาน return 'Your order is: $order'; // คืนค่าข้อมูลการสั่งซื้อ } // ฟังก์ชั่นจำลอง ข้อมูลการสั่งซื้อ Future<String> getUserOrder() { // จำลอง และสมมติว่า การทำงานส่่วนนี้มีความซับซ้อน และทำงานช้า // โดยให้ดีเลย์การคืนค่ากลับในอีก 4 วินาที return Future.delayed(Duration(seconds: 4), () => 'Large Latte'); } void main () async { // เมื่อมีการเรียกใช้ฟังก์ชั่น createOrderMessage() ในลักษณะที่ว่า เป็นฟังก์ชั่นที่ต้องรอ // โดยใช้ await กำหนดด้านหน้า print(await createOrderMessage()); // ผลลัพธ์ที่ได้ จะเป็น แสดงข้อความว่า // Your order is: Large Latte }
การจัดการ Exceptions
Exceptions เป็นส่วนที่ใช้สำหรับจัดการกับเงื่อนไขข้อผิดพลาด ที่อาจจะเกิดขึ้นได้ เพื่อให้โปรแกรมสามารถทำงานต่อไปได้
โดยอาจจะเป็นเงื่อนไขข้อยกเว้นที่เรากำหนดขึ้นเอง และใช้คำสั่ง throw เพื่อให้เข้าเงื่อนไขข้อผิดพลาดที่เกิดขึ้น
หรือเป็นค่าจากระบบที่ส่งค่ามาเมื่อเกิดข้อผิดพลาด ตัวอย่างเงื่อนไขกำหนดเอง
if (astronauts == 0) { // ถ้านักบินอวกาศเท่ากับ 0 throw StateError('No astronauts.'); // สร้างสถานะข้อผิดพลาด }
เพื่อตรวจสอบข้อผิดพลาดที่อาจจะเกิดขึ้น เราสามารถใช้คำสั่ง try ร่วมกับ on และ/หรือ catch ดังตัวอย่างดังนี้ได้
void main() { double _a = 10; double _b = 0; double _c = 0; try{ if(_b == 0){ throw StateError('Division by zero.'); } _c = _a/_b; print(_c); }catch (e){ print(e); } }
แนวทางข้างต้น เป็นเพียงหัวข้อบางส่วน และเป็นเพียงเนื้อหาทำความเข้าใจเกี่ยวกับภาษา Dart เบื้องต้นเท่านั้น เนื้อหาเพิ่มเติม
ในลำดับต่อๆ ไป รอติดตาม