เรียนรู้ภาษา Dart ตั้งแต่ตั้งไข่จนถึงคลาส (Class)
สวัสดีครับ วันนี้จะมาพูดถึงการเรียนรู้ภาษาโปรแกรมมิ่งอีกหนึ่งตัวที่มาแรงในยุคนี้ นั่นก็คือ ภาษา Dart นั่นเอง เพราะว่าผมมีความต้องการเรียนรู้การทำโมบายแอพมานานแล้ว แต่ยังติดอยู่ที่จะเริ่มเมื่อไหร่ดี จนวันเวลาผ่านมาเนิ่นนานมากแล้ว (เป็นนิสัยที่ไม่ดีนะครับ อย่าทำตามเชียว) เลยตัดสินใจขอเริ่มต้นให้ได้ซักทีเถอะ เดี๋ยวแก่ไปแล้วทำไม่ไหว
ลองเข้า Google ดู ก็พบว่า การทำแอพสมัยนี้ นิยมใช้ hybrid platfrom หรือ cross platform มากขึ้น และน่าจะเป็นเทรนด์ในอนาคตไปอีกยาวไกลแน่ๆ โดยเฉพาะเมื่อถึงปี 2020 ที่ผ่านมานั้น ยอดการใช้งานเฟรมเวิร์คมีแนวโน้มสูงขึ้นชัดเจน ด้วยความที่สะดวกต่อการพัฒนา ใช้เวลาและงบประมาณน้อย ถึงแม้ประสิทธิภาพบางอย่างอาจจะไม่เท่า native แต่ใช้ IDE ได้หลากหลาย แถมคอมมูที่ใหญ่มากขึ้นเรื่อยๆ จึงไม่น่าแปลกใจๆที่ใครก็อยากลองใช้งาน cross-platform framework ซักตัวนึงไว้ประดับเกียรติยศ
ฉะนั้นแล้ว ผมจึงขอจิ้มไปที่ Flutter ละกัน เหตุอันเนื่องมาจากข้อมูลนี้
Flutter อยู่อันดับสองของรายการ แต่เมื่อดูเปรียบเทียบ y-y จะเห็นว่า เป็นอันเดียวที่มีแนวโน้มพุ่งทะยานสุด ขณะที่หลายๆตัวแผ่วลงไป เลยขออนุญาตศึกษาตัวนี้ละกัน (ขออนุญาตใคร?)
ทำไมต้อง Dart ?
ด้วยความเป็น Flutter มันอยู่บนพื้นฐานของการใช้ Dart ในการพัฒนา ดังนั้น เราก็ต้องเรียนการใช้งานตั้งแต่ตัวภาษาไปก่อน แล้วค่อยต่อยอดไปสู่การทำแอพ ดังนั้นบทความนี้จะเป็นบันทึกจากการเรียนภาษา Dart ตั้งแต่เบื้องต้น ซึ่งผมนั้นได้เรียนจากทั้งสื่อไทยและเทศ แล้วก็เอามาสรุปตามความเข้าใจของตัวเอง ซึ่งก็ อาจจะมีส่วนผิดพลาดตามปกติวิสัยของมนุษย์ ยังไงก็ขออภัยนะครับ
เริ่มกันเลยครับ ขอข้ามส่วนพื้นฐานบางอย่างไปเพื่อจะได้ไปเน้นที่รูปแบบโครงสร้าง (paradigm) ของการทำงานเลยละกันนะครับ
เวอร์ชั่นที่เรียนรู้จะเป็น Dart 2
บทความนี้ จะขอแบ่งการเรียนรู้ของภาษาตาม syntax ออกเป็นหมวดๆ ตามนี้
- Variables and Operators
- Expressions
- Decision Making and Looping Constructs
- Comments
- Functions
- Classes
- Libraries and Packages
- Typedefs
- Data structures represented as Collections / Generics
Variables and Operators
เริ่มต้นด้วยฟังก์ชั่น main() แบบนี้
main() {
<statements>
}
ฟังก์ชั่นนี้เป็นส่วนแสดงผลของโปรแกรม ในทุกๆการ coding ของภาษา Dart เราใช้ main() ในการกำหนดตัวแปรหรือใช้ฟังก์ชั่นใดๆก็ตาม
ตัวแปร (Variable) หมายถึง การตั้งชื่อ space ในเมโมรี่ของเครื่อง แล้วบรรจุหรือกำหนดค่า (value) ให้มัน ชื่อของตัวแปรมีศัพท์เทคนิค(จะถูกเรียก)ว่า “identifier”
กฏการตั้งชื่อตัวแปร
- ห้ามเป็น keyword
- ประกอบด้วย พยัญชนะ หรือ สระ ห้ามมีสัญลักษณ์พิเศษ ได้แก่ underscore (_) และ dollar sign ($)
- ห้ามขึ้นต้นด้วยตัวเลข
- เป็น case-sensitive ยกตัวอย่าง john จะไม่เท่ากับ John
- ห้ามมีช่องว่าง เช่น “John Doe”
Variable
การประกาศตัวแปร จะมีการใช้ keyword ก่อนเสมอ แล้วตามด้วยชื่อตัวแปร
Keyword เป็นได้ทั้ง Access Modifier (var, final, const) และ Data Type
Data Type
Dart มีการประกาศตัวแปรโดยใช้ Data Type ดังนี้
Data Type | Keyword | Description |
---|---|---|
Numbers | int, double | int : เลขฐาน 10 จำนวนเต็ม / double เลขฐาน 10 ติดทศนิยม |
Strings | String | ข้อความ หรือ อักษร |
Booleans | bool | true หรือ false |
Lists | List | สร้างรายการ Array |
Maps | Map | สร้าง JSON Object |
Dynamic | dynamic | สร้างอะไรก็ได้ |
เราสามารถประกาศตัวแปรแบบใส่ type ก่อนหรือไม่ใส่ก็ได้
ถ้าระบุ type : ใช้ Data Type แล้วตามด้วยชื่อตัวแปร
data_type variable_name
หรือจะใช้ Access Modifier (final, const) ร่วมด้วยก็ได้
access_modifier data_type variable_name
ข้อควรระวัง ถ้าประกาศ var
ซ้อนกันกับ Data Type จะ error!
ถ้าไม่ระบุ type : ใช้ Access Modifier (var, final, const) แล้วตามด้วยชื่อตัวแปรเลย โดย type จะเป็นไปตามชนิดของ value ที่กำหนด
access_modifier variable_name
var hero_1 = ‘Abaddon’ //String
var id = 123 //Number
final num_list = [1,2,3]; //Lists
ข้อควรระวัง ถ้าประกาศ Access Modifier ซ้อนกันกับ Data Type จะ error!
Note 1 : เราจะสังเกตว่า ตัว Access Modifier (var) กับ Data Type (dynamic) ต่างสามารถใช้กำหนดตัวแปรชนิดไหนก็ได้ แต่ทั้งสองมีข้อแตกต่างกันตรงที่ dynamic เปลี่ยน type ได้เรื่อยๆ เช่น
dynamic a = 1; // เป็น int
a = true; // ตอนนี้เป็น bool ละ
ส่วน var กำหนดตัวแปรเป็น type อะไรแล้ว ถ้าจะเปลี่ยนค่า ก็ต้องเป็น type เดิม เช่น
var b = 2; // เป็น int
b = 3; // ยังเป็น int อยู่
b = ‘handsome’; // error เพราะเปลี่ยน type จาก int เป็น String ไม่ได้
Note 2 : ตัวแปรที่เป็น const ต่างจาก final ตรงที่ เฉพาะ const เท่านั้นจะถูกคำนวณ “compile time constant”
compile time constant เป็นค่าคงที่ที่ถูกกำหนดตอนคอมไพล์ (compile time) ใน Dart มีแค่ const ตัวเดียว ที่เหลือจะถูกสร้างตอนรัน (run time) ทั้งหมด อ่านต่อได้ที่โพสต์ของ Tamemo
Operator
คือตัวดำเนินการทางคณิตศาสตร์ที่ใช้ประเมินผล (evaluate) หรือกำหนดค่า (assign) แบ่งออกเป็นกลุ่มตามการใช้งานได้ดังนี้
Arithmetic Operators
Operator ใน Dart ก็เหมือนกับภาษาอื่นๆ คือมี + (บวก), - (ลบ), * (คูณ), / (หาร type double), ~/ (หาร type integer), % (modulo: หาเศษจากหาร) ++ (increment), -- (decrement) นอกจากนั้น ก็มี -() (Unary minus : เปลี่ยนค่าเป็นค่าตรงข้าม เช่น -(2) => 2)
Equality and Relational Operators
เอาไว้กำหนดความสัมพันธ์ระหว่างตัวแปรสองตัว เช็คค่าตัวแปร ทำให้ได้คำตอบออกมาเป็น Boolean (true, false) ได้แก่ > (มากกว่า), < (น้อยกว่า), => (เท่ากับหรือมากกว่า), =< (เท่ากับหรือน้อยกว่า), == (เท่ากับ), != (ไม่เท่ากับ)
Type test Operators
เอาไว้เช็ค type ของตัวแปร ตอน runtime ได้แก่ is และ is!
var e;
print(e is int); // false
print(e is! int); // true
แต่สำหรับ String ต้องใส่ค่าก่อนนะ (ไม่เป็น null) ไม่งั้นเช็ค type จะเป็น false
String a;
print(a is String); // false
String b = 'aaa';
print(b is String); // true
Bitwise Operators
คือ ตัวดำเนินการแบบบิต เป็นตัวดำเนินการที่ใช้กับชนิดข้อมูลเลขจำนวนเต็ม (integer) เท่านั้น โดยจะแปลงเป็นเลขฐาน 2 (8-bit) ก่อนดำเนินการ หลังจากนั้น จะแปลงค่ากลับเป็นฐาน 10 กลับมา ซึ่งมีตัวเนินการได้แก่
& (AND) เป็นการหาเลข 1 ที่ตำแหน่ง(บิด)เดียวกัน มีเงื่อนไขดังนี้
ถ้า 1&1 เป็น 1, ถ้า 1&0 เป็น 0, ถ้า 0&1 เป็น 0, ถ้า 0&0 เป็น 0
ยกตัวอย่าง
5 & 3 มีค่าเท่ากับ 1 เนื่องจาก
5 แปลงเป็นเลขฐานสองได้เป็น 0000 0101
3 แปลงเป็นเลขฐานสองได้เป็น 0000 0011
ผลลัพธ์เมื่อ AND กันแต่ละบิต ได้ 0000 0001 มีค่าเท่ากับ 1 (เลขฐานสิบ)
| (Or) เป็นการตัวเลข 1 อย่างน้อย 1 ตามตำแหน่งเข้าด้วยกัน มีเงื่อนไขดังนี้
ถ้า 1|1 เป็น 1, ถ้า 1|0 เป็น 1, ถ้า 0|1 เป็น 1, ถ้า 0|0 เป็น 0
ยกตัวอย่าง
5 | 3 มีค่าเท่ากับ 7 เนื่องจาก
5 แปลงเป็นเลขฐานสองได้เป็น 0000 0101
3 แปลงเป็นเลขฐานสองได้เป็น 0000 0011
ผลลัพธ์เมื่อ OR กันแต่ละบิต ได้ 0000 0111 มีค่าเท่ากับ 7 (เลขฐานสิบ)
^ (XOR) เป็นการตัวเลข 0 อย่างน้อย 1 ตามตำแหน่งเข้าด้วยกัน มีเงื่อนไขดังนี้
ถ้า 1^1 เป็น 0, ถ้า 1^0 เป็น 1, ถ้า 0^1 เป็น 1, ถ้า 0^0 เป็น 1
ยกตัวอย่าง
5 | 3 มีค่าเท่ากับ 6 เนื่องจาก
5 แปลงเป็นเลขฐานสองได้เป็น 0000 0101
3 แปลงเป็นเลขฐานสองได้เป็น 0000 0011
ผลลัพธ์เมื่อ XOR กันแต่ละบิต ได้ 0000 0110 มีค่าเท่ากับ 6 (เลขฐานสิบ)
~ (NOT) เป็นการกลับค่าในแต่ละตำแหน่ง จาก 1 เป็น 0 และจาก 0 เป็น 1
ยกตัวอย่าง
~3 มีค่าเท่ากับ 4294967292 เนื่องจาก
3 แปลงเป็นเลขฐานสองได้เป็น 0000 0011
3 จะถูกกลับค่าได้เป็น 1111 1100 มีค่าเท่ากับ 4294967292 (เลขฐานสิบ)
<< (Left shift) หมายถึงการเลื่อนบิตไปทางซ้ายมือ เช่น
5<<2 มีค่าเท่ากับ 20 เนื่องจาก
5 แปลงเป็นเลขฐานสองได้เป็น 0000 0101
เลื่อนบิตซ้ายสุดของตัวตั้งไปเป็นจำนวน 2 บิต ได้เป็น 00 | 0001 0100
ผลลัพธ์ของการเลื่อนบิตทางขวาของ 5<<2 คือ 0001 0100 มีค่าเท่ากับ 20
(คิดง่ายๆ คือการตัดเลขทางซ้ายสุด แล้วเอา 0 มาต่อทางขวาสุดให้ครบ 8 bit นั่นแหละ)
>> (Right shift) หมายถึงการเลื่อนบิตไปทางขวามือ เช่น
5>>2 มีค่าเท่ากับ 1 เนื่องจาก
5 แปลงเป็นเลขฐานสองได้เป็น 0000 0101
เลื่อนบิตขวาสุดของตัวตั้งไปเป็นจำนวน 2 บิต ได้เป็น 0000 0001 | 01
ผลลัพธ์ของการเลื่อนบิตทางขวาของ 5>>2 คือ 0000 0001 มีค่าเท่ากับ 1
(คือการตัดเลขทางขวาสุด แล้วเอา 0 มาต่อทางซ้ายสุดให้ครบ 8 bit นั่นแหละ)
Assignment Operators
เป็นตัวดำเนินการที่เป็นการกำหนดค่าให้ตัวแปร มีดังนี้
= (Simple Assignment)
คือการกำหนดค่าตัวแปรให้เป็นค่าต่างๆ คืนค่าเป็นค่าที่กำหนดให้
??=
กำหนดค่าให้ตัวแปรถ้าตัวแปรนั้นเป็น null ถ้าตัวแปรนั้นไม่ใช่ null ค่าของตัวแปรนั้นจะคงเดิม
+= (Add and Assignment)
คือการบวกค่าตัวแปรนั้นด้วยค่าที่กำหนด คืนค่าเป็นตัวแปรที่ผ่านการบวกแล้ว
─= (Subtract and Assignment)
คือการบวกค่าตัวแปรนั้นด้วยค่าที่กำหนด คืนค่าเป็นตัวแปรที่ผ่านการลบแล้ว
*= (Multiply and Assignment)
คือการบวกค่าตัวแปรนั้นด้วยค่าที่กำหนด คืนค่าเป็นตัวแปรที่ผ่านการคูณแล้ว
/= (Divide and Assignment)
คือการบวกค่าตัวแปรนั้นด้วยค่าที่กำหนด คืนค่าเป็นตัวแปรที่ผ่านการหารแล้ว
Logical Operators
ใช้รวมเงื่อนไขสองเงื่อนไขเข้าด้วยกัน จะคืนค่าออกมาเป็น boolean ตัวดำเนินการมีดังนี้
&& (And) จะเป็น true ถ้า ทั้งสองเงื่อนไขเป็นจริง
|| (Or) จะเป็น true ถ้า เงื่อนไขใดเงื่อนไขหนึ่งเป็นจริง
! (Not) กลับค่า boolean เช่น จาก true เป็น false
Expressions and Programming Constructs
Expression
คือ statement ที่ประเมินผลที่มาจาก Operands และ Operator ได้ผลเป็น ค่า (value) ออกมา ถ้าพิมพ์ expression บน command line interpreter จะทำการประเมินผลและแสดงผลลัพธ์ เช่น
>>> 1 + 2
3
ในตัวอย่าง Operands คือ 1 กับ 2 ส่วน Operator คือ +
แม้ว่า expression จะประกอบไปด้วย Operands และ Operator แต่ไม่จำเป็นที่ทุก expression ต้องประกอบไปด้วย elements ครบทั้งสอง แค่ Operands อย่างเดียว จะถูกพิจารณาว่าเป็น expression ได้เช่นเดียวกัน เช่น
>>> 17
17
>>> x
2
และการประเมินผลของ expression นั้นจะไม่เหมือนกับการสั่ง print โดยทีเดียว
ตัวอย่าง
>>> message = " What’s up, Doc?"
>>> message
" What’s up, Doc?"
Conditional Expressions
Syntax ของการใส่เงื่อนไขเพื่อแสดง expression นอกจาก การใช้ if….else แบบทั่วไปแล้ว ยังสามารถเขียนในรูปแบบต่างๆดังต่อไปนี้ได้อีก เช่น
condition ? expr1 : expr2
ตัวอย่าง
var a = 50;
var b = a>30 ? a+12 : a;
print(b); // output = 62
expr1 ?? expr2
ตรวจสอบว่า ถ้า expr1 ไม่ใช่ null จะคืนค่านั้นออกมา ถ้าเป็น null จะคืนค่าจาก expr2
ตัวอย่าง
var a;
var b = a ?? 800;
print(b); // output = 800
ต่อมาจะพูดถึง syntax ที่ใช้สำหรับสร้างทางเลือกเพื่อตัดสินใจ และการใช้คำสั่งวนซ้ำ
Decision Making
การสร้างทางเลือกในการตัดสินใจ โดยจะพิจารณาเงื่อนไขก่อนที่จะไปรันคำสั่งนั้นๆ
ในภาษา Dart เองก็มีการสร้างเงื่อนไขเหมือนกับภาษาอื่นๆ เช่น การใช้ if แบบปกติ
if(boolean_expression){
// statement(s) will execute if the Boolean expression is true.
}
การใช้ if...else เพื่อสร้างทางเลือก 2 ทาง ถ้าทางเลือกแรกเป็นเท็จ ถึงจะรัน statement ทางเลือกที่สอง
if(boolean_expression){
// statement(s) will execute if the Boolean expression is true.
} else {
// statement(s) will execute if the Boolean expression is false.
}
การใช้ if….if else…..else จะคล้ายกันกับ if...else แต่เป็นการสร้างทางเลือกมากกว่า 2 ทาง และจะดูเงื่อนไขเรียงลำดับไปเรื่อยๆจนถึงอันที่ถูก จากนั้นก็จะรันคำสั่ง แล้วออกจาก decision
if (boolean_expression1) {
//statements if the expression1 evaluates to true
}
else if (boolean_expression2) {
//statements if the expression2 evaluates to true
}
else {
//statements if both expression1 and expression2 result to false
}
Switch Case เป็น statement ที่ใช้สร้างทางเลือกมากกว่า 2 ทาง อีกแบบหนึ่ง โดย
- ใช้ switch เป็น statement หลัก ตามด้วยตัวแปรที่เก็บค่า expression เพื่อเข้าสู่การพิจารณาเงื่อนไข
- ใช้ case เป็นทางเลือกต่างๆ และพิจารณา case ที่จะ match กับ expression กับ switch ตามลำดับ จากบนลงล่าง
- ทางเลือกสุดท้าย จะใช้เป็น default แทน โดย ไม่ต้องมี expression เหมือนกับ case
- ถ้า case ใดเป็นจริง จะรันคำสั่งที่กำหนดใน case นั้นๆ และออกจาก statement ด้วย break
- ถ้าไม่ใช้ break กำกับต่อท้ายของแต่ละ case โปรแกรมจะไม่หยุดทำงาน และดู case ต่อไปอีก
switch(variable_expression) {
case constant_expr1: {
// statements;
}
break;
case constant_expr2: {
//statements;
}
break;
default: {
//statements;
}
break;
}
ตัวอย่าง Switch Case
print('What do you do, guys?');
String bob = 'Rob a bank.';
print(bob);
switch(bob) {
case 'Study English.' : {print('May you have a good future.');}
break;
case 'Do nothing.' : {print('It\'s up to you.');}
break;
case 'Rob a bank.' : {print('Please go to jail, Police!!!');}
break;
default : {print('Are you a OK?');}
break;
}
/*
Output
What do you do, guys?
Rob a bank.
Please go to jail, Police!!!
*/
คำสั่งวนซ้ำ (Loop)
เราสามารถจำแนกรูปแบบคำสั่งวนซ้ำ ได้ 2 ประเภทใหญ่ๆคือ
- แบบ Definite เป็นการกำหนดจำนวณรอบวนซ้ำ ซึ่งใช้กับรูปแบบ for
- แบบ Indefinite ไม่กำหนดจำนวณรอบ ซึ่งใช้กับรูปแบบ while และ do...while
รูปแบบ for
สามารถใช้ syntax ได้ 2 แบบ คือ แบบ semicolon (;) คั่น และ แบบที่ใช้ in (เหมือนภาษา Python)
1. แบบ semicolon
for (initial_count_value; termination-condition; step) {
//statements
}
initial_count_value คือ expression ระบุค่าตั้งต้น บอกให้โปรแกรมรุ้ว่าจะเริ่มที่ค่าอะไร
termination-condition คือ expression ที่เป็นจุดสิ้นสุด คำสั่งวนซ้ำจะทำงานไปจนกว่าจะเป็นเท็จ
step คือ expression ที่บอกว่าคำสั่งวนซ้ำทำงานจบลง 1 ครั้งนั้น จำนวณรอบมีการเปลี่ยนแปลงยังไง
ตัวอย่าง
var num = 5;
var factorial = 1;
for( var i = num ; i >= 1; i-- ) {
factorial *= i ;
}
print(factorial);
จากตัวอย่าง หมายความว่า คำสั่งวนซ้ำจะเริ่มทำงานที่ i = num ไปจนกว่า (i >= 1) จะเป็นเท็จ โดยที่ จำนวณรอบจะลดลงทีละ 1 (i--)
2. แบบที่ใช้ in
for (variable_name in object){
statement or block to execute
}
variable_name แทนถึง สมาชิกทุกตัวที่อยู่ใน object นั้น
ตัวอย่าง
var obj = [12,13,14];
for (var prop in obj) {
print(prop);
} // output is 12 13 14
รูปแบบ while
รูปแบบการทำงาน จะดูเงื่อนไขก่อนแล้วค่อยรันคำสั่ง
ใช้ while เป็นตัวเช็คเงื่อนไข แล้วค่อยเริ่มคำสั่ง จะทำงานวนซ้ำไปเรื่อยๆจนกว่าเงื่อนไขเป็น false
while (expression) {
Statement(s) to be executed if expression is true
}
ยกตัวอย่าง
void main() {
var num = 5;
var factorial = 1;
while(num >=1) {
factorial = factorial * num;
num--;
}
print("The factorial is ${factorial}");
}
ได้คำตอบออกมาเป็น
The factorial is 120
รูปแบบ do...while
รูปแบบการทำงาน จะรันคำสั่งก่อนแล้วค่อยดูเงื่อนไข
ใช้ do เป็นตัวเริ่มคำสั่ง แล้วใช้ while เป็นตัวเช็คเงื่อนไข จะทำงานวนซ้ำไปเรื่อยๆจนกว่าเงื่อนไขเป็น false
do {
Statement(s) to be executed;
} while (expression);
ยกตัวอย่าง
void main() {
var n = 10;
do {
print(n);
n--;
}
while(n>=0);
}
ได้คำตอบเป็น
10
9
8
7
6
5
4
3
2
1
0
Comment
คือ ข้อความที่โปรแกรมไม่สนใจ (ignored) เมื่อเวลา compile โค้ด ใช้สำหรับอธิบาย หรือบอกส่วนต่างๆของโค้ดให้ผู้พัฒนาคนอื่นๆเข้าใจได้ง่ายขึ้น
เราจะใช้ // แล้วตามด้วยข้อความ เมื่อต้องการ comment บรรทัดเดียว ส่วนถ้าหลายบรรทัด จะใช้ /*.... */
เช่น
// this is single line comment
/* This is a
Multi-line comment
*/
Function
ฟังก์ชั่นในภาษา Dart มีความคล้ายกับ Javascript ค่อนข้างมาก เช่น
รูปแบบปกติ
int add (int a, int b) {
return a + b;
}
void main() {
print(add(3,5));
} //คำตอบ 8
ตรงฟังก์ชั่น add สามารถลดรูปลงเหลือ
add (a, b) {
return a + b;
}
Arrow Function
รูปแบบ Arrow Function ก็ทำได้
add (a, b) => a + b;
Classes
การสร้าง class ใช้ keyword class มีรูปแบบดังนี้
class class_name {
<fields>
<getters/setters>
<constructors>
<functions>
}
ยกตัวอย่างการสร้างคลาส
สร้างไฟล์ class_public.dart แล้วสร้างคลาส Weapon ขึ้นมา
class Weapon {
// fields
String name;
int volume;
// getters/setters
// getters
String get weapon_name {
return name;
}
int get weapon_volume {
return volume;
}
// setters
void set weapon_name(String name) {
this.name = name;
}
void set weapon_volume(int volume) {
if(volume<=10) {
print('Go back home right now!');
} else {
this.volume = volume;
}
}
}
ในไฟล์ main.dart สร้าง function main() แบบนี้
import 'class_public.dart';
void main() {
Weapon set_1 = new Weapon();
set_1.weapon_name = 'Rocket Flare';
set_1.weapon_volume = 5;
print(set_1.weapon_name);
print(set_1.weapon_volume);
}
เมื่อกดรันจะได้ output เป็น
Go back home right now!
Rocket Flare
null
จะเห็นว่า มีการกำหนด getters/setters เพื่อกำหนดค่าให้กับตัวแปร และคืนค่าของตัวแปรที่กำหนดให้
ในส่วน main ก็มีการกำหนดค่าตัวแปรผ่าน setter และ statement ในนั้นจะทำงานทันที เมื่อกดรันโค้ดจะทำให้ข้อความในเงื่อนไขถูก print ออกมา ส่วน weapon_name กับ weapon_volume ก็ถูกสั่ง print ตามปกติ เพียงแต่ตัวแปร volume ไม่ได้ถูกเก็บค่าเอาไว้จาก statement เงื่อนไขที่อยู่ภายใน setter weapon_volume
ถ้าเราเปลี่ยนส่วนของ main เป็นแบบนี้ล่ะ
void main() {
Weapon set_1 = new Weapon();
set_1.name = 'Rocket Flare';
set_1.volume = 5;
print(set_1.weapon_name);
print(set_1.weapon_volume);
}
หรือว่าแบบนี้
void main() {
Weapon set_1 = new Weapon();
set_1.name = 'Rocket Flare';
set_1.volume = 5;
print(set_1.name);
print(set_1.volume);
}
จะได้ output เป็น
Rocket Flare
5
พบว่า เราสามารถกำหนดค่าให้ตัวแปรในคลาสได้โดยตรง ทำให้ getters/setters ไม่ทำงาน แถมยังคืนค่าออกมาเป็นค่าที่กำหนดให้ตรงๆโดยไม่ผ่าน statement เลย รู้สึกว่า คลาสของเราจะมีช่องโหว่ให้คนภายนอกมากำหนดค่าได้ตามใจ แบบนี้ไม่ดีแน่ ต้องกำหนดการทำงานใหม่ ให้สามารถกำหนดค่าได้ตรงที่ setters เท่านั้น และได้คืนค่าผ่าน getters เท่านั้น
เราลองหา keyword ที่ใช้กำหนดความเป็นส่วนตัว ก็พบว่า ไม่มี! ทำไมอ่ะ แล้วที Java ยังมี private แล้วทำไม Dart ไม่มีแบบนั้นบ้างอ่ะ ก่อนที่จะเริ่มหัวร้อนไปมากกว่านี้ เราก็ค้นพบต่อไปอีกว่า จะเป็นตัวแปรจากสถานะ public ให้เป็น private นั้น ไม่ต้องใช้คีย์ใดๆทั้งสิ้น ใช้แค่ “_” ก็พอ เช่น
เปลี่ยน name (public) เป็น _name (private)
เปลี่ยน volume (public) เป็น _volume (private)
สร้างไฟล์ class_private.dart และเปลี่ยนโค้ดใหม่เป็น
class Weapon {
// fields
String _name;
int _volume;
// getters/setters
// getters
String get weapon_name {
return _name;
}
int get weapon_volume {
return _volume;
}
// setters
void set weapon_name(String name) {
this._name = name;
}
void set weapon_volume(int volume) {
if(volume<=10) {
print('Go back home right now!');
} else {
this._volume = volume;
}
}
}
ในไฟล์ main.dart ลองกดรันดูด้วยโค้ดเดิม
void main() {
Weapon set_1 = new Weapon();
set_1.name = 'Rocket Flare';
set_1.volume = 5;
print(set_1.weapon_name);
print(set_1.weapon_volume);
}
จะได้ output เป็น
main.dart:5:9: Error: The setter 'name' isn't defined for the class 'Weapon'.
- 'Weapon' is from 'class_private.dart'.
Try correcting the name to the name of an existing setter, or defining a setter or field named 'name'.
set_1.name = 'Rocket Flare';
^^^^
main.dart:6:9: Error: The setter 'volume' isn't defined for the class 'Weapon'.
- 'Weapon' is from 'class_private.dart'.
Try correcting the name to the name of an existing setter, or defining a setter or field named 'volume'.
set_1.volume = 5;
^^^^^^
exit status 254
นั่นแสดงว่า ตัวแปร name กับ volume ไม่สามารถกำหนดค่าได้จากภายนอกได้แล้ว ต้องใช้การกำหนดค่าผ่านทาง getters เท่านั้น
เปลี่ยนโค้ดที่ main.dart เป็น
import 'class_private.dart';
void main() {
Weapon set_1 = new Weapon();
set_1.weapon_name = 'Rocket Flare';
set_1.weapon_volume = 5;
print(set_1.weapon_name);
print(set_1.weapon_volume);
}
จะได้ output เป็น
Go back home right now!
Rocket Flare
null
นี่เป็นตัวอย่างการสร้างความเป็นส่วนตัวของคลาสเพื่อความปลอดภัยของข้อมูล
นอกจากนั้น เราสามารถใช้ constructor เพื่อกำหนดค่าตัวแปร แทน setter ได้เช่นกัน
ไฟล์ class_constructor.dart
class Weapon {
// fields
String _name;
int _volume;
// getters/setters
// getters
String get weapon_name {
return _name;
}
int get weapon_volume {
return _volume;
}
// Constructor
Weapon(String name, int volume) {
this._name = name;
if(volume<=10) {
print('Go back home right now!');
} else {
this._volume = volume;
}
}
}
ที่ไฟล์ main.dart ก็ลดโค้ดไปเหลือแค่นี้เอง
import 'class_constructor.dart';
void main() {
Weapon set_1 = new Weapon('Rocket', 5);
print(set_1.weapon_name);
print(set_1.weapon_volume);
}
จะได้ output เป็นแบบเดิม
Go back home right now!
Rocket
null
จะเห็นได้ว่า ได้คำตอบเดียวกัน แต่ลดปริมาณโค้ดลง เนื่องจากตัด setters ออกแล้วใช้ constructor ให้กำหนดค่าแทน แล้วเมื่อกำหนด object ใน main ก็เพียงใส่ parameter ให้ class ไปด้วยเลย ไม่ต้องเพิ่ม statement เพื่อกำหนดค่าอีก
ตัว constructor สามารถกำหนดค่าโดยที่ไม่ต้องใช้ this ได้
Weapon(String name, int volume) {
_name = name;
if(volume<=10) {
print('Go back home right now!');
} else {
_volume = volume;
}
}
สามารถแปลงได้อีกแบบนึง โดย constructor รับ parameter เข้ามาแล้วกำหนดไว้ในตัวแปรเลย
จากนั้น แปลง if statement นิดหน่อย จะได้คำตอบออกมาเหมือนเดิม
Weapon(this._name, this._volume) {
if(_volume<=10) {
_volume = null;
print('Go back home right now!');
} else {
this._volume;
}
}
ในส่วนของ class เอง ที่จริงก็ยังมีอีกหลายๆเรื่องที่จะต้องเรียนรู้ paradigm ในส่วนอื่นๆของภาษา เช่น Class Inheritance, Method Override, Static Keyword, Super Keyword รวมถึงหัวข้ออื่นๆอีกที่เหลือ ซึ่งจะขอเอาไปต่อใน Part หน้าครับ
อ้างอิง
https://en.wikipedia.org/wiki/Dart_(programming_language)
https://www.tutorialspoint.com/dart_programming/dart_programming_variables.htm
https://dev.to/centrilliontech/dart-101-dart-l4l
https://www.woolha.com/tutorials/dart-using-access-modifiers-private-public
https://sites.google.com/site/khasangkhwbkhumporkaermjava/taw-danein-kar-operators/2
https://th.wikibooks.org/wiki/วิธีคิดแบบนักวิทยาการคอมพิวเตอร์/Variables,_expressions_and_statements
Cover