การเปรียบเทียบวิธีการ Binding Object ใน Laravel Service Container
| วิธีการ | bind() |
singleton() |
scoped() |
instance() |
|---|---|---|---|---|
| คำอธิบาย | สร้างอินสแตนซ์ใหม่ทุกครั้งที่มีการเรียกใช้ (Resolved) | สร้างอินสแตนซ์เพียงครั้งเดียว และส่งคืนอินสแตนซ์เดิมในคำขอครั้งต่อไป | สร้างอินสแตนซ์เพียงครั้งเดียวภายในแต่ละรอบการทำงานของคำขอ/งาน (Request/Job Lifecycle) และจะถูกล้างหลังจากคำขอสิ้นสุดลง | ผูก (Bind) อินสแตนซ์ที่มีอยู่แล้วเข้ากับคอนเทนเนอร์ และส่งคืนอินสแตนซ์นั้นทุกครั้งที่มีการเรียกใช้ |
| ข้อดี | - ได้อินสแตนซ์ใหม่สำหรับทุกคำขอ ไม่มีปัญหาเรื่องการแชร์สถานะ (State Sharing) - การใช้หน่วยความจำสะอาดหมดจด - เหมาะสำหรับตรรกะที่ต้องการสถานะที่เป็นอิสระ - สามารถกำหนดค่าเฉพาะสำหรับแต่ละคำขอได้ |
- ประหยัดทรัพยากรระบบโดยสร้างอินสแตนซ์เพียงครั้งเดียว - รับประกันความสม่ำเสมอของสถานะ - เหมาะสำหรับการแชร์การกำหนดค่าและทรัพยากร - ประสิทธิภาพดีกว่าในสถานการณ์ที่มีการทำงานพร้อมกันสูง (High Concurrency) |
- สมดุลระหว่างการใช้หน่วยความจำและการแชร์สถานะ - แชร์สถานะภายในคำขอเดียวกัน แต่แยกจากกันระหว่างคำขอ - เหมาะสำหรับสถานการณ์ Web Request |
- ควบคุมจังหวะการสร้างอินสแตนซ์ได้อย่างสมบูรณ์ - สามารถกำหนดค่าสถานะของอินสแตนซ์ล่วงหน้าได้ - เหมาะสำหรับบริการที่ต้องการการกำหนดค่าเฉพาะ |
| ข้อเสีย | - ต้องใช้ทรัพยากรระบบมากขึ้นในการสร้างอินสแตนซ์ซ้ำๆ - อาจส่งผลกระทบต่อประสิทธิภาพภายใต้การทำงานพร้อมกันสูง - ไม่เหมาะสำหรับสถานการณ์ที่ต้องการแชร์สถานะ - อาจทำให้เกิดปัญหาด้านประสิทธิภาพเนื่องจากมีการสร้างอินสแตนซ์ใหม่ทุกครั้ง |
- สถานะที่แชร์อาจนำไปสู่บั๊กที่ติดตามได้ยาก - ไม่เหมาะสำหรับสถานการณ์ที่ต้องการสถานะที่เป็นอิสระ - การใช้หน่วยความจำจะยังคงอยู่จนกว่าโปรแกรมจะสิ้นสุด - การทดสอบต้องใส่ใจเป็นพิเศษในการรีเซ็ตสถานะ - หากอินสแตนซ์นั้นถือสถานะไว้อาจทำให้เกิดพฤติกรรมที่ไม่คาดคิด |
- อาจทำให้เกิดปัญหาในงานที่ใช้เวลานาน (Long-running tasks) - จำเป็นต้องเข้าใจวงจรชีวิตของคำขอ (Request Lifecycle) - ไม่เหมาะสำหรับคำสั่ง CLI |
- ยืดหยุ่นน้อยกว่า - ไม่สามารถปรับการกำหนดค่าอินสแตนซ์แบบไดนามิกได้ - อาจใช้หน่วยความจำเมื่อเริ่มต้น - ไม่เหมาะสำหรับกรณีที่ต้องการ Lazy Loading หรือหลายอินสแตนซ์ |
| กรณีการใช้งาน | สำหรับบริการที่ใช้งานระยะสั้นหรือคลาสที่ต้องการสถานะที่เป็นอิสระ | สำหรับบริการที่แชร์ทั่วโลก เช่น Logger หรือ Configuration Manager | สำหรับบริการที่ต้องการรักษาสถานะภายในคำขอ แต่ไม่ต้องการข้ามคำขอ เช่น การเชื่อมต่อฐานข้อมูล | เมื่อคุณมีอินสแตนซ์อยู่แล้วและต้องการนำกลับมาใช้ใหม่ เช่น อ็อบเจกต์การกำหนดค่าที่แชร์ |
| ตัวอย่าง | เมื่อคุณมีคลาส Product และต้องการสร้างอินสแตนซ์ Product ใหม่ทุกครั้งที่มีการขอข้อมูลผลิตภัณฑ์ที่แตกต่างกัน |
เมื่อคุณมีคลาส Logger และต้องการให้การดำเนินการเขียนบันทึกทั้งหมดใช้อินสแตนซ์เดียวกันเพื่อรักษาความสม่ำเสมอ |
เมื่อคุณมีคลาส ShoppingCart และต้องการแชร์สถานะตะกร้าสินค้าภายในคำขอเดียวกัน |
เมื่อคุณมีคลาส DatabaseConfig และต้องการตั้งค่าพารามิเตอร์การเชื่อมต่อเมื่อแอปพลิเคชันเริ่มทำงาน |
ตัวอย่าง
การใช้ bind()
$this->app->bind('Product', function () {
return new Product();
});
ทุกครั้งที่เรียก app('Product') จะส่งคืนอินสแตนซ์ Product ใหม่
การใช้ singleton()
$this->app->singleton('Logger', function () {
return new Logger();
});
ทุกครั้งที่เรียก app('Logger') จะส่งคืนอินสแตนซ์ Logger ตัวเดิม
การใช้ scoped()
$this->app->scoped(Transistor::class, function (Application $app) {
return new Transistor($app->make(PodcastParser::class));
});
แชร์อินสแตนซ์เดียวกันภายในวงจรชีวิตของคำขอเดียวกัน คำขอที่แตกต่างกันจะสร้างอินสแตนซ์ใหม่
การใช้ instance()
$service = new Transistor(new PodcastParser);
$this->app->instance(Transistor::class, $service);
ผูกอินสแตนซ์ที่มีอยู่แล้วเข้ากับคอนเทนเนอร์โดยตรง
สรุป
ปัจจุบัน Laravel สามารถนำไปใช้ในสถานการณ์ต่างๆ มากมาย เช่น Web Request, CLI Command, Queue Job, Console Command เป็นต้น
ดังนั้น ในการเลือกใช้วิธีการ Binding แบบใด คุณต้องพิจารณาความต้องการของบริการในสถานการณ์ต่างๆ และพิจารณาว่าคุณจำเป็นต้องแชร์สถานะหรือการกำหนดค่าหรือไม่