OOP in Dart — มาเรียนรู้ OOP ผ่าน Dart กันเถอะ [ep.2 : Constructor in Dart]

Art Chawarat
6 min readMar 11, 2024

--

ไม่ได้เขียนมานานเลยนะครับสำหรับ series OOP in Dart แต่ปีนี้ผมตั้งใจจะมาเขียนต่อให้จบครับ ถึงจะไม่มีคนมาอ่านก็เถอะ😂

ครับ ใน ep. ที่แล้วเราได้ไปทำความรู้จัก Class และ Object รวมถึงการนำไปใช้งานจริงกันแล้วนะครับ ใน ep. นี้ครับ ผมจะพาทุกคนมาเรียนรู้เกี่ยวกับ Constructor ใน Dart กัน มันคืออะไรและมีความสำคัญอย่างไร ไปดูกันเลยครับ!!!

Constructor in Dart

Constructor มันคือ method พิเศษที่ใช้สำหรับการ initialize หรือกำหนดค่าเริ่มต้นของ object โดยมันจะเรียกใช้งานอัตโนมัติเมื่อ object ตัวหนึ่งๆถูกสร้างขึ้นมา เดี๋ยวเราไปดูตัวอย่างการเรียกใช้ constructor

Animal cat = Animal("Tom", "Orange", "Siamese Cat", 2);

จากตัวอย่างเป็นการสร้าง object จาก Class Animal และมีการ initial ค่าให้กับ object นี้คือ name ให้ชื่อว่า Tom, color ให้มีสี Orange, breed ให้เป็นพันธุ์ Siamese Cat และ age มีอายุ 2 เดือน

หากไม่มีการใช้งาน Constructor

ถ้าเราไม่มีการประกาศ Constructor สำหรับ Class เมื่อเราต้องการ set ค่าต่างๆของ object เราจะต้องทำการตั้งค่าโดยสร้าง object ของ class ที่ต้องการมาก่อน หลังจากนั้นจึงค่อย set ทีละค่าให้กับ object นั้นตามตัวอย่างนี้

Animal cat = Animal();
cat.name = "Tom";
cat.color = "Orange";
cat.breed = "Siamese Cat";
cat.age = 2;

สิ่งควรรู้

ข้อที่เราควรรู้เกี่ยวกับ Constructor นั้นมีอยู่สองข้อหลักๆคือ

  1. Constructor จะมีชื่อเดียวกับชื่อของ Class
  2. Constructor จะไม่มีการ return ของ

เพื่อความเข้าใจมากขึ้นเรามาดูตัวอย่างของ constructor กันเลยดีกว่าครับ

//class Animal
class Animal {
//attributes
String? name;
int? age;

//constructor
Animal(String name, int age) {
//initial ค่าให้ object
this.name = name;
this.age = age;
}
}

//main
void main() {
//ส่วนที่เรียกใช้ constructor
Animal cat = Animal("Tom", 2);
}

เรามาดูแต่ละส่วนของ code โดยละเอียดกันน

ส่วนที่ 1: ส่วนของ class

//class Animal
class Animal {
//attributes
String? name;
int? age;
...
}

ส่วนนี้จะเป็น class ครับ ซึ่งก็คือพิมพ์เขียวของการสร้าง object โดย class นี้มีชื่อว่า Animal และมีการประกาศ attributes 2 ตัวคือ name และ age เป็นคุณสมบัติของ class Animal นี้

ส่วนที่ 2: ส่วนของ constructor

class Animal {
...
//constructor
Animal(String name, int age) {
//initial ค่าให้ object
this.name = name;
this.age = age;
}
}

ส่วนนี้จะเป็น method ที่มีชื่อเดียวกับ class และไม่มีการ return ค่าใดออกมา แต่มีการ initial ค่าให้ object ของ class นี้ นี่แหละครับที่เราเรียกว่า constructor ซึ่ง constructor จาก code นี้จะมีการรับค่าเป็น name และ age มาด้วย และจะมีการใช้

this.name = name และ this.age = age เป็นการ initial ค่าที่ส่งมาให้กับ constructor นี้ให้กับตัว object ที่เรียกใช้ constructor ตัวนี้

อาจฟังดูงงๆ เดี๋ยวผมจะมาขยายความของเจ้า this ตัวนี้เป็นภาพกัน

ขยายความการใช้ this

‘this’ หมายความว่า ‘นี้’ ซึ่งเราจะใช้ this. ตามด้วยชื่อตัวแปรที่เป็น attribute ของ class เพื่อให้เข้าใจว่ามันคือค่าของ attribute ของ class นี้นะ ไม่ใช่ค่าจากที่อื่น

แล้วการที่เราเอา this.name = name ก็หมายถึง เราต้องการ set ให้ตัวแปร name ของ class นี้ มีค่าเท่ากับ การ initial ค่า name ผ่าน constructor เมื่อเราประกาศ object ขึ้นมานั่นเอง

เพื่อความเข้าใจมากขึ้น เราไปดูส่วนที่ 3 ที่เราประกาศใช้งาน constructor กันต่อเลย

ส่วนที่ 3: ส่วนที่เรียกใช้ constructor

//main
void main() {
//ส่วนที่เรียกใช้ constructor
Animal cat = Animal("Tom", 2);
}

ในส่วนนี้จะเป็นส่วนที่เรียกใช้ constructor ที่เราประกาศไว้ใน class ซึ่งมันก็จะเหมือนการประกาศ object ธรรมดา แต่มีส่วนที่พิเศษคือการที่เพิ่มการ initial ค่าให้ object ด้วย จาก code นี้คือ (“Tom”, 2) ซึ่ง 2 ค่านี้ก็มาจาก constructor ที่ประกาศไว้ใน class ที่ว่าเราต้องการ set ให้ค่าของ this.name = name และ this.age = age นั่นเอง

โดย “Tom” ตัวแรกก็คือ name ที่ส่งเข้าไปใน constructor นั่นแหละ และ 2 ก็คือ age ที่ส่งเข้าไปพร้อมๆกัน

ถ้าเราอยากทดสอบว่าค่า name คือ Tom และ age คือ 2 เนี่ยมันถูก set ให้กับ cat จริงๆหรือเปล่าเราก็สามารถ print ค่าออกมาดูได้ครับโดยทำตาม code นี้เลย

//main
void main() {
//ส่วนที่เรียกใช้ constructor
Animal cat = Animal("Tom", 2);
//ทดสอบการ set ค่า
print("Name: ${cat.name}");
print("Age: ${cat.age}");
}

จาก code เราจะเพิ่มการ print ค่า name และ age ของ cat ออกมา ซึ่งผลลัพธ์ก็จะเป็นดังภาพนี้ครับ

ทดสอบการ print ค่า name, age

เห็นไหมล่ะครับว่ามันมีการ set ค่า name = “Tom” และ age = 2 ลงไปใน cat ที่มี class แม่คือ Animal

และนี่แหละครับคือ constructor ตัวช่วยที่สามารถ initial ค่าของ object ได้

ประเภทของ constructor

เราได้รู้การใช้งาน constructor คร่าวๆไปบ้างแล้วนะครับ ซึ่ง constructor ของ dart เนี่ยนะครับก็สามารถเขียนได้หลายประเภท แต่ละประเภทก็จะเหมาะกับการใช้งานที่แตกต่างกัน เรามาดูกันเลยว่ามันเขียนแบบไหนได้บ้าง

  1. Default Constructor

constructor แบบ default เป็น constructor ที่ถูกสร้างขึ้นอัตโนมัติเมื่อ dart ถูก compile มันจะเกิดขึ้นเองด้วยถ้าเราไม่ได้มีการประกาศ constructor ไว้ โดย default constructor จะไม่มีพารามิเตอร์ มาดูตัวอย่างของ constructor ประเภทนี้กัน

// class Animal
class Animal {
// attributes
String? name;
int? age;

// Constructor
Animal() {
print("This is a default constructor");
}
}

void main() {
//ส่วนที่เรียกใช้ constructor
Animal cat = Animal();
}

หากรันออกมาจะได้ดังนี้

จากภาพ เมื่อมีการสร้าง object ของ class Animal ก็จะมีการเรียกใช้ default constructor ด้วยโดยอัตโนมัติ จึงมีการ print “This is a default constructor” ออกมานั่นเอง

2. Parameterized Constructor

constructor แบบนี้ใช้เพื่อ initial ค่าต่างๆให้กับ class โดยแบบนี้จะเป็นแบบที่เบสิคที่สุดในการเขียน ซึ่ง Parameterized Constructor ก็สามารถเขียนได้หลายรูปแบบครับ ทั้งเขียนแบบเต็มและเขียนแบบ Short Form ดังตัวอย่างข้างล่างครับ

//class Animal
class Animal {
//attributes
String? name;
int? age;

//constructor
Animal(String name, int age) {
//initial ค่าให้ object
this.name = name;
this.age = age;
}
}

//main
void main() {
//ส่วนที่เรียกใช้ constructor
Animal cat = Animal("Tom", 2);
}

Parameterized Constructor แบบปกติ

//class Animal
class Animal {
//attributes
String? name;
int? age;

// Constructor in single line
Animal(this.name, this.age);
}

//main
void main() {
//ส่วนที่เรียกใช้ constructor
Animal cat = Animal("Tom", 2);
}

Parameterized Constructor แบบ Short Form

เราสามารถพัฒนาการเขียน constructor จากแบบปกติเป็นแบบ short form นี้ได้ โดยไม่ต้องไปประกาศ this.name = name อีกรอบ แต่เราเขียนให้ใช้ this.name ได้เลย ซึ่งแบบนี้ก็จะให้ผลลัพธ์เหมือนการเขียนแบบแรก แต่จะใช้บรรทัดที่น้อยกว่า และดูสบายตามากขึ้น

3. Parameterized Constructor แบบ Named Parameter

นี่ก็เป็นหนึ่งในประเภทของ Parameterized Constructor ที่มีการเพิ่ม Named Parameter เข้ามา โดยการเขียนปีกกา {} ครอบ Parameter แบบเดิมไว้ ซึ่งความพิเศษของ Constructor แบบนี้คือ เมื่อเรามีการสร้าง object และมีการกำหนดค่าให้ constructor จะต้องมีการกำหนดชื่อของ attributes ใน class นั้นด้วย ไม่ใช่การเขียนแค่ค่าลอยๆขึ้นมา ซึ่งก็จะทำให้เราเข้าใจมากขึ้นว่าเรา initial ค่าไหนใน class บ้าง ดังตัวอย่างข้างล่าง

class Animal {
String? name;
int? age;

// Constructor
Animal({String? name, int? age}) {
this.name = name;
this.age = age;
}
}

void main(){
// object cat ที่สร้างมาจาก class Animal
Animal cat = Animal(name: "Tom", age: 2);
// มี name: และ age: เพิ่มเข้ามา
}

4. Parameterized Constructor แบบที่มี Default Value ด้วย

constructor แบบนี้จะเหมือน constructor แบบปกตินั่นแหละครับ แค่มีการกำหนดค่าเริ่มต้นหรือ Default Value ให้กับ class นั้นเมื่อครั้งที่มีคนสร้าง object ขึ้นมาด้วยเท่านั้นเอง

class Animal{
String? name;
int? age;

// Constructor
Animal({String? name = "Tom", int? age = 2}) {
this.name = name;
this.age = age;
}
}

void main(){
// object cat ที่สร้างมาจาก class Animal
Animal cat = Animal();
}

ถ้าเราลอง print ค่าหลังจากการสร้าง object cat ขึ้นมาก็จะเห็นว่ามีค่าสำหรับ name และ age แล้วด้วย จากการที่เรา initial ค่าตั้งแต่ใน constructor ที่เขียนใน class

5. Named Constructor

constructor แบบนี้จะเป็นแบบที่พิเศษหน่อยของ Dart ครับ เพราะในหลายๆภาษาโปรแกรมมิ่งทั้ง Java, C++, C# นั้นสามารถสร้างได้หลาย constructor ที่มีชื่อเดียวกัน แต่ Dart ไม่สามารถทำได้ หากเราต้องการสร้าง constructor ที่มีชื่อเดียวกันหลายๆตัวเราจึงต้องใช้ named constructor แทน

เรามาดูตัวอย่างการใช้งาน Named Constructor กัน

class Animal {
String? name;
int? age;

// Default Constructor
Animal() {
print("This is a default constructor");
}

// Named Constructor
Animal.namedConstructor(String name, int age) {
this.name = name;
this.age = age;
}
}

void main() {
Animal cat = Animal.namedConstructor("Tom", 2);

}

จาก code ด้านบนจะเห็นว่าใน Class Animal มีการประกาศไว้ 2 attribute คือ name, age และมี 2 constructors โดย constructor แรกจะเป็น default constructor ส่วน constructor ต่อมาคือ named constructor ที่มีหน้าที่ initial ค่าให้กับ object cat ที่ set ค่าให้มี name เป็น Tom และ age เป็น 2

มาดูกันว่าส่วนมากแล้ว Named Constructor เอาไปใช้ยังไงกันครับ

import 'dart:convert';

class Animal {
String? name;
int? age;

Animal(this.name, this.age);

Animal.fromJson(Map<String, dynamic> json) {
name = json['name'];
age = json['age'];
}

Animal.fromJsonString(String jsonString) {
Map<String, dynamic> json = jsonDecode(jsonString);
name = json['name'];
age = json['age'];
}
}

void main() {
String jsonString1 = '{"name": "Tom", "age": 2}';
String jsonString2 = '{"name": "Jerry", "age": 1}';

Animal cat1 = Animal.fromJsonString(jsonString1);
print("First Animal name: ${cat1.name}");
print("First Animal age: ${cat1.age}");

Animal cat2 = Animal.fromJsonString(jsonString2);
print("Second Animal name: ${cat2.name}");
print("Second Animal age: ${cat2.age}");
}

จากตัวอย่าง เราจะมี Class Animal ที่มี 2 attributes คือ name, age และมี 3 constructors คือ

1. Parameterized constructor แบบสั้น ที่ใช้ initial ค่า name และ age

2. Named constructor ที่ชื่อ fromJson มีหน้าที่สร้าง object ของ class Animal จาก JSON

3. Named constructor ชื่อ fromJsonString ที่ใช้สร้าง object จาก class Animal จาก JSON string

และเราจะมีการสร้าง object cat1, cat2 มาจาก Class Animal

6. Constant Constructor

Constructor ประเภทนี้ถูกสร้างขึ้นมาเพื่อ object ที่จะมีค่าเป็นค่าคงที่และไม่มีการเปลี่ยนแปลงค่า การประกาศ constructor ประเภทนี้จะมีการใช้ keyword “const” โดยข้อดีของ Constructor แบบนี้ก็คือ ทำให้ program ของเรามี performance ที่ดีขึ้น

กฏของการประกาศ Constant Constructor ใน Dart

  1. Attributes ทั้งหมดของ Class ต้องไม่มีการเปลี่ยนแปลงค่าในภายหลัง (ใช้ keyword final นำหน้าในการประกาศ attribute)
  2. Constructor นี้ต้องไม่มี body
  3. เฉพาะ Class ที่มี const เท่านั้น ที่จะมีการประกาศ object โดยใช้ const นำหน้า

มาดูตัวอย่างการใช้งานกัน

class Animal {
final String? name;
final String? breed;
final int? age;

// Constant Constructor
const Animal({this.name, this.breed, this.age});
}

void main() {
const Animal cat = Animal(name: "Tom", breed: "Siamese Cat", age: 2);
}

ก็จบไปแล้วนะครับสำหรับ ep.2 Constructor ของเรา หวังว่าบทความนี้จะเป็นประโยชน์ไม่มากก็น้อยสำหรับผู้อ่านนะครับ ep. หน้าจะเขียนเรื่องอะไร แล้วจะมีคนมาติดตามอ่านต่อหรือไม่ โปรดรอรับชมกันนะครับ😂 สำหรับวันนี้ขอขอบคุณและสวัสดีครับ

--

--