- vừa được xem lúc

Golang Design Patterns - Composite. Không phải là kế thừa.

0 0 25

Người đăng: Kha Leo

Theo Viblo Asia

Sau series về nhóm mẫu thiết kế khởi tạo, chúng ta cùng đến với nhóm mẫu thiết kế thứ hai là structural design patterns (nhóm cấu trúc), một mẫu thiết kế thể hiện cách mà các object hay class được liên kết với nhau tạo nên một cấu trúc lớn hơn để có thể đáp ứng nhiều yêu cầu hơn nhưng không kém phần linh hoạt. Chúng ta cùng đến với design pattern đầu tiên: Composite Design Pattern

I. Composite - Structural Pattern

Khi gặp các bài toán có yêu cầu thiết kế cấu trúc phân cấp hay dạng cây, nơi mà một đối tượng chứa rất nhiều đối tượng bên trong nó, cũng như có các thuộc tính và phương thức theo riêng từng đối tượng, chúng ta thường nghĩ ngay đến Composite. Mẫu thiết kế này giúp chúng ta giải quyết rất nhiều vấn đề về kế thừa và đa kế thừa.

Composite vận dụng mối quan hệ has-a, thay vì is-a như kế thừa. Nói thêm một ít về 2 loại relationship này, is-a relationship là kế thừa, nghĩa là các đối tượng kế thừa được xem là sub hoặc child từ đối tượng cha. Mặc khác has-a tạo ra một đối tượng có tham chiếu đến đối tượng khác. Có thể nói Apple is-a Fruit, nhưng chỉ có thể nói School has-a Student, composite được dùng khi bạn muốn tái sự dụng code cho hai đối tượng không cùng loại với nhau, và ngược lại với kế thừa.

II. Composite mang lại cho developers những gì?

Composite giúp chúng ta làm việc với cấu trúc dữ liệu dạng cây một cách dễ dàng hơn khi tận dụng được tính đa hình và đệ quy để xử lý các bài toán của nó. Hơn nữa, với nguyên tắc đóng/mở, bạn có thể thêm những loại đối tượng mới vào cấu trúc cây này mà không làm ảnh hưởng đến cấu trúc hiện tại.

III. Ví dụ thực tế

Chúng ta sẽ đến với bài toán đơn giản nhất là tổ chức cây thư mục cho hệ điều hành, yêu cầu bài toán như sau:

  • Cấu trúc thư mục bao gồm FolderFile gọi chung là component
  • Một folder có thể bao gồm nhiều folders và files khác
  • Folder và File bao gồm field name và method getName, setName và print (in cấu trúc thư mục từ vị trí component hiện tại)
  • Folder có thêm method add (có thể thêm folder và file khác)

Testcase mẫu

Root
|--FolderA |--FileA |--FileB |--FolderX |--FileY |--FolderZ |--FileW
|--FolderB |--FileC

Với yêu cầu bài toán như trên, chúng ta cần đáp ứng những yêu cầu sau với Composite design pattern:

  • Folder gồm field là name và các method getName, setName, print và add.
  • File gồm field name và các method getName, setName, print
  • Folder và File không có mối quan hệ kế thừa với nhau, dù có các field và method giống nhau

IV. Implementation

Trước tiên chúng ta bắt đầu định nghĩa struct File và Folder, như sau:

package composite import ( "fmt"
) type File struct { name string
} func (m *File) GetName() string { return m.name
} func (m *File) SetName(name string) { m.name = name
} func (m *File) Print(args ...interface{}) { fmt.Println(m.GetName())
}
package composite import ( "fmt" "log" "strings"
) type Folder struct { name string components []Component
} func (m *Folder) GetName() string { return m.name
} func (m *Folder) SetName(name string) { m.name = name
} func (m *Folder) Print(args ...interface{}) { fmt.Println(m.name) nested := 0 if len(args) > 0 { var ok bool nested, ok = args[0].(int) if !ok { log.Fatal("first argument must be a number") } } for _, s := range m.components { fmt.Printf("%s%s%s", strings.Repeat(" ", nested), strings.Repeat(" ", nested), "|--") s.Print(nested + 1) }
} func (m *Folder) Add(c ...Component) { m.components = append(m.components, c...)
} 

Vậy là xong bước định nghĩa struct cho File và Folder, ở đây chúng ta thấy method Add của Folder nhận vào đối số là một danh sách Component. Như yêu cầu đặt ra, chúng ta dùng Component như một đối tượng chung cho File và Folder, vì hai đối tượng này không có quan hệ kế thừa với nhau.

Component struct định nghĩa như sau:

package composite type Component interface { GetName() string Print(args ...interface{})
} 

Đến bước chạy chương trình, chúng ta cùng khai báo các files và folders theo như test case phía trên (test case hơi dài nên code cũng như thế 😤)

package main import ( "fmt" composite "github.com/structural-patterns/composite"
) func main() { /* Example Composite */ fmt.Println("*** Example Composite ***") root := &composite.Folder{} root.SetName("Root") folderA := &composite.Folder{} folderA.SetName("FolderA") fileA := &composite.File{} fileA.SetName("FileA") fileB := &composite.File{} fileB.SetName("FileB") folderX := &composite.Folder{} folderX.SetName("FolderX") fileY := &composite.File{} fileY.SetName("FileY") folderZ := &composite.Folder{} folderZ.SetName("FolderZ") fileW := &composite.File{} fileW.SetName("FileW") folderZ.Add(fileW) folderX.Add(fileY, folderZ) folderA.Add(fileA, fileB, folderX) folderB := &composite.Folder{} folderB.SetName("FolderB") fileC := &composite.File{} fileC.SetName("FileC") folderB.Add(fileC) root.Add(folderA, folderB) root.Print() fmt.Print("*** End of Composite ***\n\n\n")
} 

Chạy chương trình và kết quả thu được:

V. Lời kết

Sử dụng Composite khi chúng ta nhận thấy rằng chúng ta đang sử dụng chức năng của các đối tượng theo cùng một cách, và mã nguồn của chúng gần giống như nhau. Tuy nhiên, một khi sự khác nhau giữa các đối tượng là quá nhiều, thì việc vận dụng mẫu thiết kế này sẽ trở nên phức tạp hơn, khó hiểu hơn. Tất cả là phụ thuộc vào lựa chọn của chúng ta nhé 😄

Cảm ơn các bạn đã xem bài viết.

Bình luận

Bài viết tương tự

- vừa được xem lúc

gRPC - Nó là gì và có nên sử dụng hay không?

Nhân một ngày rảnh rỗi, mình ngồi đọc lại RPC cũng như gRPC viết lại để nhớ lâu hơn. Vấn đề là gì và tại sao cần nó .

0 0 132

- vừa được xem lúc

Embedded Template in Go

Getting Start. Part of developing a web application usually revolves around working with HTML as user interface.

0 0 57

- vừa được xem lúc

Tạo Resful API đơn giản với Echo framework và MySQL

1. Giới thiệu.

0 0 61

- vừa được xem lúc

Sử dụng goquery trong golang để crawler thông tin các website Việt Nam bị deface trên mirror-h.org

. Trong bài viết này, mình sẽ cùng mọi người khám phá một package thu thập dữ liệu có tên là goquery của golang. Mục tiêu chính của chương trình crawler này sẽ là lấy thông tin các website Việt Nam bị deface (là tấn công, phá hoại website, làm thay đổi giao diện hiển thị của một trang web, khi người

0 0 237

- vừa được xem lúc

Tạo ứng dụng craw dữ liệu bing với Golang, Mysql driver

Chào mọi người . Lâu lâu ta lại gặp nhau 1 lần, để tiếp tục series chia sẻ kiến thức về tech, hôm nay mình sẽ tìm hiểu và chia sẻ về 1 ngôn ngữ đang khá hot trong cộng đồng IT đó là Golang.

0 0 76

- vừa được xem lúc

Golang: Rest api and routing using MUX

Routing with MUX. Let's create a simple CRUD api for a blog site. # All . GET articles/ .

0 0 55