1. Flyweight Pattern là gì
1.1 Định nghĩa và Khái niệm
Flyweight Pattern là một mẫu thiết kế cấu trúc có mục đích chính là tối thiểu hóa số lượng đối tượng được chia sẻ, từ đó tiết kiệm bộ nhớ và cải thiện hiệu suất. Mẫu thiết kế Flyweight giảm thiểu việc tạo ra và tiêu thụ đối tượng bằng cách chia sẻ các đối tượng giống nhau hoặc tương tự, đạt được tối ưu hiệu suất.
1.2 Sự khác biệt so với các Mẫu thiết kế khác
So với các mẫu thiết kế khác, Flyweight Pattern chủ yếu tập trung vào việc chia sẻ và tái sử dụng đối tượng. Nó chia các đối tượng thành trạng thái nội bộ có thể chia sẻ và trạng thái bên ngoài không thể chia sẻ. Bằng cách chia sẻ trạng thái nội bộ, nó giảm thiểu việc tạo ra và tiêu thụ bộ nhớ của đối tượng, cải thiện hiệu quả của hệ thống.
2. Đặc điểm và Lợi ích của Flyweight Pattern
Các đặc điểm và lợi ích chính của Flyweight Pattern bao gồm:
- Sử dụng bộ nhớ được tối thiểu: Nó giảm thiểu tiêu thụ bộ nhớ bằng cách chia sẻ các đối tượng giống nhau hoặc tương tự.
- Cải thiện hiệu suất: Nó giảm thiểu việc tạo ra và hủy đối tượng, tăng tốc hoạt động của hệ thống.
- Hỗ trợ một lượng lớn các đối tượng tinh mịch: Nó có thể tạo ra một lượng lớn các đối tượng tinh mịch mà không chiếm quá nhiều không gian bộ nhớ.
- Giảm thiểu cấu trúc hệ thống: Bằng cách phân chia các trạng thái nội và trạng thái bên ngoài của đối tượng, nó giản đơn hóa cấu trúc và phức tạp của hệ thống.
3. Ví dụ về Ứng dụng Thực tế của Flyweight Pattern
Flyweight Pattern có thể được áp dụng trong các kịch bản sau:
- Đối tượng hạt trong trò chơi: Các thuộc tính của mỗi đối tượng hạt có thể được chia thành trạng thái nội và trạng thái bên ngoài, và các đối tượng hạt cùng các thuộc tính có thể được chia sẻ.
- Đối tượng kết nối trong máy chủ mạng: Các thuộc tính của đối tượng kết nối có thể được chia thành trạng thái nội và trạng thái bên ngoài, và các đối tượng kết nối hiện có có thể được tái sử dụng trước khi bị thu hồi.
4. Triển khai của Flyweight Pattern trong Golang
4.1 Sơ đồ lớp UML
Sơ đồ lớp UML của Flyweight Pattern trong Golang như sau:
4.2 Giới thiệu Ví dụ
Trong ví dụ này, chúng ta sẽ tạo một bộ chỉnh sửa đồ họa dựa trên Flyweight Pattern, bao gồm các hình tròn có màu sắc khác nhau, và giảm sử dụng bộ nhớ bằng cách chia sẻ các đối tượng hình tròn cùng màu sắc.
4.3 Bước Triển khai
4.3.1 Tạo giao diện Flyweight và Lớp Concrete Flyweight
Đầu tiên, chúng ta cần tạo một giao diện Flyweight
để xác định các hoạt động của các đối tượng được chia sẻ. Sau đó, chúng ta có thể tạo một lớp ConcreteFlyweight
để triển khai giao diện Flyweight
và bao gồm trạng thái nội.
// Flyweight định nghĩa giao diện của các đối tượng flyweight
type Flyweight interface {
Operation(extrinsicState string)
}
// ConcreteFlyweight đại diện cho đối tượng flyweight cụ thể, triển khai giao diện Flyweight
type ConcreteFlyweight struct {
intrinsicState string
}
// Operation triển khai phương thức hoạt động của đối tượng được chia sẻ
func (f *ConcreteFlyweight) Operation(extrinsicState string) {
fmt.Printf("Đối tượng flyweight cụ thể, trạng thái nội: %s, trạng thái ngoại: %s\n", f.intrinsicState, extrinsicState)
}
4.3.2 Tạo lớp FlyweightFactory
Tiếp theo, chúng ta có thể tạo một lớp FlyweightFactory
để quản lý và chia sẻ các đối tượng flyweight. Lớp nhà máy này duy trì một từ điển flyweights
để lưu trữ các đối tượng flyweight đã tạo.
// Lớp FlyweightFactory
type FlyweightFactory struct {
flyweights map[string]Flyweight
}
// GetFlyweight truy xuất hoặc tạo một đối tượng flyweight từ nhà máy
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 Ví dụ Gọi từ Khách hàng
Cuối cùng, chúng ta có thể tạo một lớp Client
để minh họa cách sử dụng mẫu flyweight để triển khai một bộ chỉnh sửa đồ họa.
// Lớp Client
type Client struct {
flyweight Flyweight
}
// Operation thực hiện một phép hoạt động
func (c *Client) Operation() {
c.flyweight.Operation("trạng thái ngoại")
}
4.4 Quyền lợi và Thực tiễn Triển khai
4.4.1 Chia sẻ Trạng thái và An toàn Luồng
Khi sử dụng mẫu flyweight, cần chú ý đến việc chia sẻ trạng thái nội bộ và an toàn luồng. Vì các đối tượng flyweight được chia sẻ bởi nhiều khách hàng, cần đảm bảo tính nhất quán của trạng thái nội bộ.
4.4.2 Quản lý Object Pool
Để quản lý và tái sử dụng các đối tượng flyweight một cách hiệu quả hơn, có thể sử dụng object pool để lưu trữ các đối tượng flyweight đã được tạo. object pool có thể tăng tỷ lệ tái sử dụng đối tượng và giảm overhead của việc tạo và hủy đối tượng.
4.4.3 Quản lý Trạng thái Object Bên Ngoài
Mẫu flyweight tách biệt trạng thái nội bộ và trạng thái bên ngoài của các đối tượng, nhưng trạng thái bên ngoài cần được quản lý bởi khách hàng. Khi sử dụng đối tượng flyweight, khách hàng cần truyền trạng thái bên ngoài cho đối tượng flyweight để thực hiện các thao tác.
Đoạn mã Ví dụ hoàn chỉnh
Dưới đây là một đoạn mã Golang ví dụ hoàn chỉnh:
package main
import "fmt"
// Flyweight định nghĩa giao diện của một đối tượng flyweight
type Flyweight interface {
Operation(extrinsicState string)
}
// ConcreteFlyweight biểu diễn một đối tượng flyweight cụ thể và triển khai giao diện Flyweight
type ConcreteFlyweight struct {
intrinsicState string
}
// Operation triển khai phương thức hoạt động cho các đối tượng được chia sẻ
func (f *ConcreteFlyweight) Operation(extrinsicState string) {
fmt.Printf("Đối tượng flyweight cụ thể, trạng thái nội bộ: %s, trạng thái bên ngoài: %s\n", f.intrinsicState, extrinsicState)
}
// Lớp FlyweightFactory
type FlyweightFactory struct {
flyweights map[string]Flyweight
}
// GetFlyweight truy xuất hoặc tạo một đối tượng flyweight từ nhà máy
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
}
// Lớp Khách hàng
type Client struct {
flyweight Flyweight
}
// Operation thực hiện một thao tác
func (c *Client) Operation() {
c.flyweight.Operation("trạng thái bên ngoài")
}
func main() {
factory := &FlyweightFactory{
flyweights: make(map[string]Flyweight),
}
flyweight1 := factory.GetFlyweight("A")
flyweight1.Operation("trạng thái bên ngoài 1")
flyweight2 := factory.GetFlyweight("B")
flyweight2.Operation("trạng thái bên ngoài 2")
client := &Client{
flyweight: factory.GetFlyweight("A"),
}
client.Operation()
}