ภาษา Go ตอน 3 รู้จักกับ Defer และ Struct

Chaiyarin Niamsuwan
odds.team
Published in
4 min readAug 23, 2018

ต่อยอดจากตอนที่ 2 ที่เราได้เรียนรู้ Syntax พื้นฐานของ ภาษา Go กันแล้ว ใครยังไม่อ่านตอนที่ 2 สามารถอ่านได้จาก Link ข้างล่างนี้ครับ

คราวนี้ผมจะพาไปทำความรู้จักกับ การใช้งาน Defer และ Struct ว่ามันคืออะไรนะ ทำไมต้องใช้ และใช้ยังไง

เปิดเว็บ The Go Playground แล้วไปลุยกันเลยย

Defer คืออะไร

Defer ก็คือ การสั่งให้ Function หรือ Method ใดๆ ก็ตามที่เรากำหนดไว้ ทำงานหลัง Main Function ทำงานเสร็จ

พูดง่ายๆ ก็คือ Function หรือ Method ที่เรากำหนดให้เป็น Defer จะทำงานหลังสุด ก่อนโปรแกรมจะปิดไปนั่นเอง

ทำไมต้องใช้ Defer

จริงๆแล้ว Defer เหมาะสำหรับ การประกาศให้ Function นั้นๆ เก็บกวาดความเรียบร้อยของโปรแกรมของเรา เช่น

สมมติว่า เรามีการเปิด Connection ต่อฐานข้อมูล Mysql เมื่อเรา จัดการกับฐานข้อมูลเสร็จแล้วไม่ว่าจะเป็นการ Insert, Update, Delete

เราก็ต้องมีการปิด Connection ที่ต่อกับ Mysql ด้วย นี่หละ ที่ Defer จะคอยสร้างความมั่นใจให้เราว่า

ก่อนที่โปรแกรมเราจะปิดตัวลง จะมีการ ปิด Connection ที่ต่อจาก Mysql ให้เราอย่างแน่นอน

เพราะบางทีถ้าเราไม่ปิด Connection อะไรบางอย่างหลังจากโปรแกรมทำงานจบ Connection เหล่านั้นอาจจะจองพื้นที่อยู่ใน Ram ของเรา ทำให้ Ram เราเต็มได้

การใช้งาน Defer

งั้นเราลองมาเขียนโปรแกรม ทดลองการใช้งาน Defer กันครับ โดยเริ่มจาก สร้าง Function ซัก 3 Function แบบนี้

[ยังไม่มีการใช้งาน Defer]package mainimport (
"fmt"
)
func main() {
printFirst();
printFinish();
printSecond();
}
func printFirst() {
fmt.Println("First");
}
func printSecond() {
fmt.Println("Second");
}
func printFinish() {
fmt.Println("Close");
}
[ผลลัพธ์จากการ Run ด้วย Source Code นี้]First
Close
Second

จะเห็นว่า Function Main ก็ทำงานไล่จากบนลงล่างตามปกติ ซึ่งก็ถูกต้องแล้ว จาก Code หมายความว่า

ใน Function Main จะไปเรียก Function printFirst() ก่อน และตามมาด้วย printFinish() และ printSecond() ตามลำดับ

แต่ถ้าเรานำ Defer มาใช้งาน เราจะได้ผลลัพธ์ที่ต่างออกไป ดังเช่น Source Code ตัวนี้

[มีการใช้งาน Defer]package mainimport (
"fmt"
)
func main() {
printFirst();
defer printFinish(); // มีการเพิ่ม Defer มาที่ Function นี้
printSecond();
}
func printFirst() {
fmt.Println("First");
}
func printSecond() {
fmt.Println("Second");
}
func printFinish() {
fmt.Println("Close");
}
[ผลลัพธ์จากการ Run ด้วย Source Code นี้]First
Second
Close

จาก Souce Code จะเห็นได้ว่ามีการใช้งาน Defer ในบรรทัดของ Function printFinish()

ซึ่งเรื่อง Function Main ถูกทำงาน มันจะยังคงทำ Function printFirst() เป็นอันดับแรก

แต่ว่าข้าม Function printFinish() ไปทำ Function printSecond() ก่อน หลังจากสิ้นสุด Function Main แล้ว

ถึงจะมาทำ Function printFinish() อย่างที่บอกไปข้างต้น Defer จะทำท้ายสุดก่อนโปรแกรมจะปิดการทำงาน

แล้วถ้ามี Defer กำหนดไว้หลาย Function หละ ลำดับการทำงานจะเป็นยังไง

package main
import (
"fmt"
)
func main() {
printFirst();
defer printThird(); // -> ลำดับที่ 3
defer printFourth(); // -> ลำดับที่ 2
defer printFinish(); // -> ลำดับที่ 1
printSecond();
}
func printFirst() {
fmt.Println("First");
}
func printSecond() {
fmt.Println("Second");
}
func printFinish() {
fmt.Println("Close");
}
func printThird() {
fmt.Println("Third");
}
func printFourth() {
fmt.Println("Fourth");
}
[ผลลัพธ์ที่ได้จากการ RUN Defer แบบหลายตัว]First
Second
Close
Fourth
Third

จะเห็นได้เลยว่า มันจะทำ Defer ที่ประกาศไว้ต่ำที่สุดก่อน ณ ที่นี้ Defer ที่กำหนดไว้ต่ำที่สุดของบรรทัดก็คือ ที่ Function printFinish() ตามมาด้วย printFourth() และสุดท้ายที่ทำคือ printThird() ครับ

Struct คืออะไร

Struct จริงๆ แล้วมันคือประเภทของข้อมูลประเภทหนึ่ง ที่รวมลักษณะต่างๆของข้อมูลเอาไว้ด้วยกัน

พูดง่ายๆ คือเหมือนกับ เราต้องการจะสร้าง ก้อนข้อมูล ของ “ลูกค้า” เราจะต้องประกอบไปด้วยอะไรบ้าง ก็จะมี ชื่อ, นามสกุล, รหัสลูกค้า, เบอร์โทรศัพท์ จริงๆมันก็จบละเรื่อง Struct

แต่อาจจะยังไม่เห็นภาพ เราลองมาดู Source Code กันดีกว่าว่า มันใช้ยังไงครับ

[หน้าตาการสร้าง Struct]package mainimport "fmt"type customer struct { // การประกาศโครงสร้าง struct
firstname string
lastname string
code int
phone string
}
func main() { cus := customer{
firstname: "Chaiyarin",
lastname: "Niamsuwan",
code: 111990,
phone: "085661234",
}; // การกำหนดค่าเริ่มต้น ให้ customer struct
fmt.Println(cus);}[ผลลัพธ์ที่ได้จากการแสดงตัวแปร cus]{Chaiyarin Niamsuwan 111990 085661234}

จาก Source Code จะเห็นว่า หน้าตามันจะคล้ายๆ การสร้างโครงสร้างของตัวแปรที่เป็น Object ในภาษาอื่น

ต่อมาเราลองมาดูกันว่าถ้าจะ แสดง Field ทีละ Field ใน Struct ของ customer เราจะทำอย่างไร

[แสดง Field ทีละ Field ใน Struct]fmt.Println(cus.firstname);
fmt.Println(cus.lastname);
fmt.Println(cus.code);
fmt.Println(cus.phone);
[ผลลัพธ์จากการแสดงค่า]Chaiyarin
Niamsuwan
111990
085661234

เราสามารถเปลี่ยนแปลงค่าของ ตัวแปร cus ได้ด้วยนะ

[แสดง Field ทีละ Field ใน Struct]cus.firstname = "Atikom";
cus.lastname = "Sombutjalearn"
fmt.Println(cus);[ผลลัพธ์จากการแสดงค่าจากตัวแปร cus]{Atikom Sombutjalearn 111990 085661234}

ใช้ Struct กับ Array

ลองมาดูกันครับว่า ถ้าเราจะเอา โครงสร้างข้อมูลที่เป็น Struct ของเรานั้นมาใช้คู่กับ Array เพื่อเก็บข้อมูลเป็น List จำนวน 3 ช่อง ทำยังไงกันนะ

[การใช้ Struct กับ Array]customerLists := [3]customer{};customerLists[0] = customer{
firstname: "Chaiyarin",
lastname: "Niamsuwan",
code: 111990,
phone: "085661234",
};
customerLists[1] = customer{
firstname: "Atikom",
lastname: "Sombutjalearn",
code: 111991,
phone: "085664321",
};
customerLists[2] = customer{
firstname: "Kritsana",
lastname: "Punyaphon",
code: 111992,
phone: "085662344",
};
fmt.Println(customerLists);[ผลลัพธ์จากการแสดงค่า customerLists][
{Chaiyarin Niamsuwan 111990 085661234}
{Atikom Sombutjalearn 111991 085664321}
{Kritsana Punyaphon 111992 085662344}
]

ง่ายมากเลยใช่ไหมครับ งั้นลองมาดูกันต่อว่า คราวนี้เราจะเอา Struct มาใช้กับ Slice ได้ยังไง

ใช้ Struct กับ Slice

ใครจำ Slice ไม่ได้ว่ามันคืออะไร กลับไปอ่าน ตอน 2 ก่อนนะครับ แต่พูดคร่าวๆ ก็คือ List ที่สามารถเก็บค่าได้ไม่จำกัด ต่อยาวไปเรื่อยๆ มาดู Code กันครับ

[การใช้ Struct กับ Slice]customerLists := []customer{};customer1 := customer{
firstname: "Chaiyarin",
lastname: "Niamsuwan",
code: 111990,
phone: "085661234",
};
customer2 := customer{
firstname: "Atikom",
lastname: "Sombutjalearn",
code: 111991,
phone: "085664321",
};
customer3 := customer{
firstname: "Kritsana",
lastname: "Punyaphon",
code: 111992,
phone: "085662344",
};
customerLists = append(customerLists, customer1);
customerLists = append(customerLists, customer2);
customerLists = append(customerLists, customer3);
fmt.Println(customerLists);[ผลลัพธ์จากการแสดงค่า customerLists][
{Chaiyarin Niamsuwan 111990 085661234}
{Atikom Sombutjalearn 111991 085664321}
{Kritsana Punyaphon 111992 085662344}
]

ผลลัพธ์ที่แสดงออกมา เราจะได้ List ของ Customer เหมือน Array เลย แต่ว่าเราสามารถที่จะใช้คำสั่ง Append เพิ่มค่า Customer ใน List ไปได้เรื่อยๆ เลยครับ

มาถึงขนาดนี้ เอาให้สุดครับ อย่าไปหวั่น ในเมื่อเราได้ List ของ Customer เหล่านี้มาแล้ว จะแปลงให้มันเป็น JSON

เพื่อให้มันเอาไปทำอะไรต่ออะไรได้ยังไงดีหละ ตามมาดูกันต่อเลยครับ

การนำ List ที่ได้จาก Array หรือ Slice แปลงเป็น JSON

[การทำ List จาก Array หรือ Slice เป็น JSON]package mainimport "fmt"
import "encoding/json" // มีการ Import Encoding JSON เข้ามา
type customer struct { // มีการปรับ Field ให้ขึ้นต้นตัวใหญ่
Firstname string
Lastname string
Code int
Phone string
}
func main() { customerLists := []customer{};
customer1 := customer{
Firstname: "Chaiyarin",
Lastname: "Niamsuwan",
Code: 111990,
Phone: "085661234",
};
customer2 := customer{
Firstname: "Atikom",
Lastname: "Sombutjalearn",
Code: 111991,
Phone: "085664321",
};
customer3 := customer{
Firstname: "Kritsana",
Lastname: "Punyaphon",
Code: 111992,
Phone: "085662344",
};
customerLists = append(customerLists, customer1);
customerLists = append(customerLists, customer2);
customerLists = append(customerLists, customer3);
customerListsJson, err := json.Marshal(customerLists)

if err != nil {
fmt.Println(err)
}

fmt.Println(string(customerListsJson));
}[ผลลัพธ์ที่ได้จากการแสดงค่าจากตัวแปร customerListsJson][{"Firstname":"Chaiyarin","Lastname":"Niamsuwan","Code":111990,"Phone":"085661234"},{"Firstname":"Atikom","Lastname":"Sombutjalearn","Code":111991,"Phone":"085664321"},{"Firstname":"Kritsana","Lastname":"Punyaphon","Code":111992,"Phone":"085662344"}]

อธิบาย Source Code สักนิด คืองี้ ถ้า Copy Code ตรงนี้ไป Run มันก็จะแสดงผลลัพธ์ เป็น JSON แล้ว จาก customerLists โดยใช้ Function json.Marshal() ในการแปลง Array to JSON

และเราก็นำ JSON ตัวนี้ไปต่อยอดเป็น Response API หรือ จะเก็บลง File หรือจะ เอาไปใช้ประโยชน์อย่างอื่นได้อย่างเต็มที่

แต่ๆๆๆๆๆ มันมีอะไรหลายๆ อย่างเปลี่ยนไป เช่น

  • การกำหนด Field ใน Struct Customer ทำไมต้องขึ้นด้วยตัวใหญ่ เมื่อใช้ json.Marshal นะ ?
  • การ Import คืออะไร เอามาจากตรงไหนของระบบ ?
  • แล้วเราจะสร้าง Library ให้คนอื่นใช้ยังไง ?
  • go get -d github ที่ค้างไว้ตอน 1 มันใช้ยังไงกันแน่นะ ?

รอติดตามได้ใน ตอนที่ 4 “การใช้งาน Package ในภาษา Go” จะมาตอบคำถามทั้งหมดเหล่านี้ ครับ ฮึบบ

--

--