Chào các bạn, trong bài viết này, chúng ta sẽ đi tìm hiểu về một design pattern khá thú vị, đó là Composite pattern. Trong tiếng anh thì từ composite có nghĩa là làm một hỗn hợp, tức là một thứ gì đó được tạo từ rất nhiều thành phần hỗn tạp. Vậy cụ thể design pattern này là gì, được ứng dụng như thế nào thì bây giờ chúng ta sẽ cùng tìm hiểu
1. Composite Pattern là gì?
Composite Pattern là một mẫu cấu trúc (Structural Pattern).
Composite Pattern cho phép tương tác với tất cả các đối tượng tương tự nhau giống như là các đối tượng đơn hoặc collections.
Nếu bạn đã làm quen với cấu trúc dữ liệu dạng tree, mỗi thành phần trên tree chúng ta có thể gọi là các Nodes, mỗi node sẽ có duy nhất một node cha và có không hoặc nhiều node con. Trường hợp node mà không có node con thì được gọi là lá
Điều này tương tự đối với Composite Pattern, những element có con được gọi là Nodes, những element không có con được gọi là Leafs.
Trong thực tế thì chúng ta gặp nhiều bài toán cần dùng đến cấu trúc như vậy như hệ thống các thư mục, file, hệ thống các thẻ tag trong html, xml,…
2. Các thành phần cơ bản
Composite Pattern bao gồm các thành phần:
- Component protocol
- Leaf
- Composite
- Client
Hình minh họa các thành phần cơ bản trong composite pattern
Trong biểu đồ trên chúng ta có thể quan sát thấy:
- Component:
- Khai báo interface hoặc abtract chung cho các thành phần đối tượng.
- Chứa các method thao tác chung của các thành phần đối tượng.
- Leaf:
- Biểu diễn các đối tượng lá trong thành phần đối tượng.
- Composite:
- Định nghĩa một thao tác cho các thành phần có thành phần con.
- Lưu trữ các thành con.
- Thực thi sự quản lý các thành phần con của Component.
- Client:
- Điều khiển các đối tượng trong Composite thông qua các method trong Component.
3. Ví dụ demo
Đặt vấn đề:
Chúng ta sẽ mô phỏng lại cấu trúc phân cấp của 1 file html. Một file html sẽ bắt đầu bằng thẻ html tiếp sau đó là tập hợp các thẻ khác, mỗi thẻ có thể là parent tag hoặc child tag tùy vào tính huống cụ thể. Mục đích của chúng ta là tạo ra cấu trúc phân cấp và hiển thị cấu trúc phân cấp này ra ngoài màn hình
Trong ví dụ này chúng ta sẽ sử dụng interface để khai báo các phương thức được dùng trong các composite và leaf.
component.go
package composite
type hmtlTag interface {
SetStartTag(string)
SetEndTag(string)
GenerateHtml()
}
Trong interface hmtlTag
chúng ta liệt kê các method đều sẽ được dùng bởi các lớp composite và leaf.
Bây giờ chúng ta sẽ tạo đối tượng composite như sau
composite.go
package composite
import "fmt"
type HtmlParentElement struct {
StartTag string
EndTag string
Tags []hmtlTag
}
func (tag *HtmlParentElement) SetStartTag(name string) {
tag.StartTag = name
}
func (tag *HtmlParentElement) SetEndTag(name string) {
tag.EndTag = name
}
func (tag *HtmlParentElement) AddChildTag(htmlTag hmtlTag) {
tag.Tags = append(tag.Tags, htmlTag)
}
func (tag *HtmlParentElement) GenerateHtml() {
fmt.Println(tag.StartTag)
for _, t := range tag.Tags {
t.GenerateHtml()
}
fmt.Println(tag.EndTag)
}
HtmlParentElement
là đối tượng composite và chúng ta cần khai báo các method để implement interface hmtlTag
leaf.go
package composite
import "fmt"
type HtmlElement struct {
TagBody string
StartTag string
EndTag string
}
func (tag *HtmlElement) SetStartTag(name string) {
tag.StartTag = name
}
func (tag *HtmlElement) SetEndTag(name string) {
tag.EndTag = name
}
func (tag *HtmlElement) SetTagBody(content string) {
tag.TagBody = content
}
func (tag *HtmlElement) GenerateHtml() {
fmt.Printf("%s%s%s\n", tag.StartTag, tag.TagBody, tag.EndTag)
}
HtmlElement
là đối tượng leaf. Tương tự như với HtmlParentElement, chúng ta cũng cần khai báo các method để implement interface hmtlTag
main.go
package main
import (
"composite-pattern/composite"
)
func main() {
// Tạo thẻ html
htmlTag := composite.HtmlParentElement {
StartTag: "<html>",
EndTag: "</html>",
}
// Tạo thẻ body
var bodyTag = composite.HtmlParentElement {
StartTag: "<body>",
EndTag: "</body>",
}
htmlTag.AddChildTag(&bodyTag)
// Tạo thẻ heading
var headingTag = composite.HtmlElement {
StartTag: "<h2>",
EndTag: "</h2>",
}
headingTag.SetTagBody("Xin chào các bạn")
bodyTag.AddChildTag(&headingTag)
// Tạo thẻ para
var paraTag = composite.HtmlElement {
StartTag: "<p>",
EndTag: "</p>",
}
paraTag.SetTagBody("Các bạn có khỏe không")
bodyTag.AddChildTag(¶Tag)
htmlTag.GenerateHtml()
}
Trong file main.go
chúng ta tạo ra 1 số thẻ html, body, h2, p. Trong đó body nằm trong html, h2 và p nằm trong body
Bây giờ chạy go run main.go
và kiểm tra kết quả nào
Kết quả chúng ta có như sau:
<html>
<body>
<h2>Xin chào các bạn</h2>
<p>Các bạn có khỏe không</p>
</body>
</html>
Khi nào dùng Composite Pattern
- Khi chúng ta muốn tạo ra các đối tượng trong các cấu trúc cây để biểu diễn hệ thống phân lớp.
- Áp dụng khi nhóm đối tượng phải hoạt động như một đối tượng duy nhất
Ưu điểm
- Giúp chúng ta có thể làm việc với các cấu trúc cây phức tạp trở lên thuận tiện hơn
- Tuân thủ quy tắc "Open/Closed Principle" : Chúng ta có thể dễ dàng thêm một đối tượng mới vào ứng dụng mà không cần phải thay đổi code hiện có
Nhược điểm
- Chúng ta khó có thể cung cấp interface cho các lớp mà chức năng của nó khác nhau quá nhiều
Bình luận