1. แบบแพ็ตเทิร์น Flyweight
1.1 ความหมายและแนวคิด
แบบแพ็ตเทิร์น Flyweight เป็นแบบแพ็ตเทิร์นทางโครงสร้างซึ่งจุดประสงค์หลักคือการลดจำนวนของวัตถุที่ถูกแชร์ ซึ่งจะช่วยประหยัดหน่วยความจำและปรับปรุงประสิทธิภาพ แบบแพ็ตเทิร์น Flyweight ลดการสร้างและการใช้งานวัตถุโดยการแชร์วัตถุที่เหมือนหรือคล้ายกัน เพื่อเพิ่มประสิทธิภาพของระบบ
1.2 ความแตกต่างจากแบบแพ็ตเทิร์นอื่น ๆ
เมื่อเทียบกับแบบแพ็ตเทิร์นอื่น ๆ มากขึ้น แบบแพ็ตเทิร์น Flyweight ให้ความสำคัญกับการแชร์และการนำไปใช้วัตถุ โดยแบ่งวัตถุเป็นสถานะภายในที่สามารถแชร์และสถานะภายนอกที่ไม่สามารถแชร์ โดยการแชร์สถานะภายในจะลดการสร้างและการใช้หน่วยความจำของวัตถุ ซึ่งจะเพิ่มประสิทธิภาพให้กับระบบ
2. ลักษณะและข้อดีของแบบแพ็ตเทิร์น Flyweight
คุณลักษณะและข้อดีหลักของแบบแพ็ตเทิร์น Flyweight ประกอบด้วย:
- การใช้หน่วยความจำที่ลดลง: ลดการใช้หน่วยความจำโดยการแชร์วัตถุที่เหมือนหรือคล้ายกัน
- ปรับปรุงประสิทธิภาพ: ลดการสร้างและทำลายวัตถุ เพื่อเพิ่มความเร็วของการทำงานของระบบ
- รองรับจำนวนวัตถุระดับละเอียดที่มาก: สามารถสร้างจำนวนมากของวัตถุระดับละเอียดโดยไม่ต้องใช้พื้นที่หน่วยความจำมาก
- โครงสร้างระบบที่เรียบง่าย: โดยการแยกสถานะภายในและภายนอกของวัตถุ ทำให้สับัสำรวงโครงสร้างและความซับซ้อนของระบบ
3. ตัวอย่างการประยุกต์ใช้แบบแพ็ตเทิร์น Flyweight ในการใช้งานจริง
แบบแพ็ตเทิร์น Flyweight สามารถประยุกต์ใช้ในสถานการณ์ต่อไปนี้:
- วัตถุอนุภาคในเกม: คุณสมบัติของแต่ละวัตถุอนุภาคสามารถถูกแบ่งเป็นสถานะภายในและภายนอก และแบ่งแชร์วัตถุอนุภาคที่มีคุณสมบัติเดียวกัน
- วัตถุการเชื่อมต่อในเซิร์ฟเวอร์เครือข่าย: คุณสมบัติของวัตถุการเชื่อมต่อสามารถถูกแบ่งเป็นสถานะภายในและภายนอก และสามารถนำวัตถุการเชื่อมต่อที่มีอยู่มาใช้อีกรอบก่อนที่จะถูกทำลาย
4. การประยุกต์ใช้แบบแพ็ตเทิร์น Flyweight ใน Golang
4.1 ไดอะแกรมคลาส UML
ไดอะแกรมคลาส UML ของแบบแพ็ตเทิร์น Flyweight ใน Golang ดังนี้:
4.2 แนะนำตัวอย่าง
ในตัวอย่างนี้ เราจะสร้างตัวแก้วกราฟิคโดยใช้แบบแพ็ตเทิร์น Flyweight ประกอบด้วยวงกลมสีต่าง ๆ และลดการใช้หน่วยความจำด้วยการแชร์วัตถุวงกลมที่มีสีเดียวกัน
4.3 ขั้นตอนการประยุกต์ใช้
4.3.1 สร้างอินเตอร์เฟซ Flyweight และคลาส Concrete Flyweight
ก่อนอื่น เราต้องสร้างอินเตอร์เฟซ Flyweight
เพื่อกำหนดการดำเนินการของวัตถุที่ถูกแชร์ จากนั้นเราสามารถสร้างคลาส ConcreteFlyweight
เพื่อประมวลผลอินเตอร์เฟซ Flyweight
และรวมสถานะภายใน
// Flyweight กำหนดอินเตอร์เฟซของวัตถุแบบ flyweight
type Flyweight interface {
Operation(extrinsicState string)
}
// ConcreteFlyweight แทนวัตถุ flyweight ที่เป็นประการจริง โดยการประมวลผลอินเตอร์เฟซ Flyweight และรวมสถานะภายใน
type ConcreteFlyweight struct {
intrinsicState string
}
// Operation ประมวลผลวิธีการของวัตถุที่ถูกแชร์
func (f *ConcreteFlyweight) Operation(extrinsicState string) {
fmt.Printf("วัตถุ flyweight ที่เป็นที่ สถานะภายใน: %s, สถานะภายนอก: %s\n", f.intrinsicState, extrinsicState)
}
4.3.2 สร้างคลาส FlyweightFactory
ต่อมา เราสามารถสร้างคลาส FlyweightFactory
เพื่อการจัดการและแบ่งปันวัตถุ flyweight คลาสนี้จะรักษาพจนานุกรม flyweights
เพื่อเก็บวัตถุ flyweight ที่ถูกสร้าง
// คลาส FlyweightFactory
type FlyweightFactory struct {
flyweights map[string]Flyweight
}
// GetFlyweight เรียกดูหรือสร้างวัตถุ flyweight จากโรงงาน
func (f *FlyweightFactory) GetFlyweight(key string) Flyweight {
if fw, ok := f.flyweights[key]; ok {
return fw
}
flyweight := &ConcreteFlyweight{
intrinsicState: key,
}
f.flyweights[key] = flyweight
return flyweight
}
4.3.3 ตัวอย่างการเรียกใช้ของไคลเอ็นต์
สุดท้าย เราสามารถสร้างคลาส Client
เพื่อสาธิตวิธีการใช้แบบแพ็ตเทิร์น Flyweight เพื่อสร้างกราฟิคเอ็ดิเตอร์
// คลาส Client
type Client struct {
flyweight Flyweight
}
// Operation ทำการดำเนินการ
func (c *Client) Operation() {
c.flyweight.Operation("สถานะภายนอก")
}
4.4 ข้อคิดและการปฏิบัติในการประยุกต์ใช้
อํานวยความสะดวกและหลักการที่ดีที่สุดในการประยุกต์ใช้
4.4.1 การแบ่งปันสถานะและความปลอดภัยของเธรด
เมื่อใช้รูปแบบ flyweight pattern จำเป็นต้องใส่ใจถึงการแบ่งปันสถานะภายในและความปลอดภัยของเธรด โดยเนื่องจากอ็อบเจกต์ flyweight ถูกแบ่งปันโดยลูกค้าหลายคน จึงจำเป็นต้องรักษาความสอดคล้องของสถานะภายใน
4.4.2 การจัดการกับ Object Pool
เพื่อจัดการและนำกลับมาใช้อ็อบเจกต์ flyweight ให้ดียิ่งขึ้น สามารถใช้ object pools เพื่อเก็บอ็อบเจกต์ flyweight ที่สร้างขึ้น การใช้ object pools สามารถเพิ่มอัตราการนำกลับของอ็อบเจกต์ และลดภาระของการสร้างและทำลายอ็อบเจกต์
4.4.3 การจัดการสถานะภายนอกของ Object
รูปแบบ flyweight แยกสถานะภายในและสถานะภายนอกของอ็อบเจกต์ แต่สถานะภายนอกต้องถูกจัดการโดยลูกค้า ในกรณีที่ใช้อ็อบเจกต์ flyweight ลูกค้าจำเป็นต้องส่งสถานะภายนอกไปยังอ็อบเจกต์ flyweight เพื่อดำเนินการ
ตัวอย่างโค้ดสมบูรณ์
ด้านล่างนี้เป็นตัวอย่างโค้ด Golang ที่สมบูรณ์:
package main
import "fmt"
// Flyweight กำหนดอินเตอร์เฟซของอ็อบเจกต์ flyweight
type Flyweight interface {
Operation(extrinsicState string)
}
// ConcreteFlyweight แทนอ็อบเจกต์ flyweight ที่เฉพาะเจาะจงและประมวลแบบอินเตอร์เฟซ Flyweight
type ConcreteFlyweight struct {
intrinsicState string
}
// Operation ประมวลวิธีสำหรับอ็อบเจกต์ที่ถูกแบ่งปัน
func (f *ConcreteFlyweight) Operation(extrinsicState string) {
fmt.Printf("โอบเจกต์ flyweight ที่เฉพาะเจาะจง สถานะภายใน: %s, สถานะภายนอก: %s\n", f.intrinsicState, extrinsicState)
}
// FlyweightFactory คลาส
type FlyweightFactory struct {
flyweights map[string]Flyweight
}
// GetFlyweight เรียกคืนหรือสร้างอ็อบเจกต์ flyweight จากโรงงาน
func (f *FlyweightFactory) GetFlyweight(key string) Flyweight {
if fw, ok := f.flyweights[key]; ok {
return fw
}
flyweight := &ConcreteFlyweight{
intrinsicState: key,
}
f.flyweights[key] = flyweight
return flyweight
}
// Client คลาส
type Client struct {
flyweight Flyweight
}
// Operation ดำเนินการ
func (c *Client) Operation() {
c.flyweight.Operation("สถานะภายนอก")
}
func main() {
factory := &FlyweightFactory{
flyweights: make(map[string]Flyweight),
}
flyweight1 := factory.GetFlyweight("A")
flyweight1.Operation("สถานะภายนอก 1")
flyweight2 := factory.GetFlyweight("B")
flyweight2.Operation("สถานะภายนอก 2")
client := &Client{
flyweight: factory.GetFlyweight("A"),
}
client.Operation()
}