ฟังก์ชันครอบฟังก์ชัน

บอกตรงๆ ผมตั้งชื่อบทความนี้ไม่ถูกเลยทีเดียว เราลองมาดูกันครับ
เริ่มต้นจากความต้องการก่อนนะครับว่าผมต้องการอะไร สิ่งที่ผมต้องการคือ http handler ตัวหนึ่ง แต่เนื่องจากเมื่อวันเวลาผ่านไป ผมก็เริ่มมี handler เพิ่มขึ้นเรื่อยๆ และมีงานหลายๆอย่างที่ทำซ้ำๆเกิดขึ้น เช่นเขียน log เป็นต้น ทำให้ผมเริ่มคิดอยากจะห่อเจ้า handler ด้วยฟังก์ชันที่จะมาช่วยทำงานซ้ำๆแบบปิดหัวปิดท้ายได้ จึงเกิดบทความนี้ครับ เพื่อไม่ให้เสียเวลา มาดูโค้ดก่อนเลยดีกว่า

package main

import (
	"github.com/ant0ine/go-json-rest/rest"
	"log"
	"net/http"
)

func run(fn func(w rest.ResponseWriter, r *rest.Request)) rest.HandlerFunc {
	return rest.HandlerFunc(func(w rest.ResponseWriter, r *rest.Request) {
			println("1")
			defer println("3")
			fn(w,r)
		})
}

func action(w rest.ResponseWriter, r *rest.Request) {
			println("2")
  			w.WriteJson("Hi")
  		}

func main() {
    handler := rest.ResourceHandler{}
    err := handler.SetRoutes(
        &rest.Route{"GET", "/hi", run(action)},
    )
    if err != nil {
        log.Fatal(err)
    }
    log.Fatal(http.ListenAndServe(":8080", &handler))
}

จากตัวอย่างนี้เมื่อลองรันดูจะได้ hanlder ตัวหนึ่ง ทดสอบโดยเปิด browser แล้วใส่ url ตามนี้ http://localhost:8080/hi
ที่หน้าเว็บจะพบคำว่า “Hi” ครับ ทีนี้ลองไปดูที่หน้าจอดำๆของเราจะพบเลข 1 2 3 รวม 3 บรรทัดครับ โดยเลข 2 นั้นเกิดในฟังก์ชัน action ซึ่งเป็นตัว hanlder จริงๆของเรา ส่วน 1 และ 3 เกิดจากฟังก์ชัน run ที่เราใช้ห่อ handler และคืนค่าเป็น rest.HandlerFunc
ซึ่งถูกประกาศไว้แบบนี้ type HandlerFunc func(ResponseWriter, *Request)
ขั้นตอนการเขียนคือ

1 เขียน hanlder ปกติ ซึ่งในที่นี้คือ action
2 เขียน run โดยรับพารามิเตอร์เดียวที่มี type เดียวกับ action ของเรา
3 เขียน return ค่าออกมาเป็นฟังก์ชัน HandlerFunc โดยเราแทรก println(“1″) ไว้ และ defer println(“3″) จะทำก่อนออกจากฟังก์ชัน
4 ใช้ run ครอบ action ไว้
เพียงเท่านี้ เราก็จะสามารถนำงานซ้ำๆที่ต้องทำก่อนและหลัง handler มาไว้ที่เดียวกันได้แล้ว
ผมอาจจะอธิบาย งงๆ ไปหน่อย ดีที่สุดคือลองเขียน ลองรันดูครับ หวังว่าจะทำให้เข้าใจได้ไม่ยาก