Mẫu Factory
Thuộc phần Lập trình hướng đối tượng trong hành trình GO của Coddy. Bài học 87 trên 107.
Mẫu Factory giao việc tạo đối tượng cho một hàm (function) hoặc phương thức riêng, cho phép bạn tạo instance mà không để lộ logic khởi tạo. Khác với Singleton, vốn kiểm soát số lượng instance, Factory tập trung vào việc tạo đối tượng linh hoạt dựa trên các tham số đầu vào.
Trong Go, hàm khởi tạo thường là một hàm trả về kiểu giao diện, cho phép bạn tạo các kiểu cụ thể khác nhau thông qua một API thống nhất:
type Notifier interface {
Send(message string) string
}
type EmailNotifier struct{}
type SMSNotifier struct{}
func (e EmailNotifier) Send(message string) string {
return "Email: " + message
}
func (s SMSNotifier) Send(message string) string {
return "SMS: " + message
}
func NewNotifier(notifierType string) Notifier {
switch notifierType {
case "email":
return EmailNotifier{}
case "sms":
return SMSNotifier{}
default:
return EmailNotifier{}
}
}Hàm nhà máy NewNotifier gói gọn quyết định về kiểu cụ thể cần tạo. Code phía máy khách chỉ làm việc với giao diện Notifier và không cần biết cách triển khai cụ thể:
notifier := NewNotifier("sms")
result := notifier.Send("Hello!") // "SMS: Hello!"Mẫu này phát huy hiệu quả khi bạn cần tạo đối tượng dựa trên các điều kiện lúc chạy, muốn tập trung logic khởi tạo phức tạp vào một nơi hoặc cần dễ dàng thêm các kiểu mới mà không sửa code phía client. Chỉ cần thêm một struct triển khai interface và cập nhật hàm factory.
Thử thách
DễHãy xây dựng một hệ thống chuyển đổi tài liệu bằng mẫu Factory nhé! Bạn sẽ tạo một factory tạo ra các bộ chuyển đổi tài liệu khác nhau dựa trên định dạng đích, giúp code phía máy khách làm việc với bất kỳ bộ chuyển đổi nào thông qua một giao diện thống nhất.
Bạn sẽ sắp xếp code trong hai tệp:
converter.go: Định nghĩa giao diện bộ chuyển đổi và các phần triển khai cụ thể.Tạo giao diện
Convertervới phương thứcConvert(content string) string, nhận nội dung tài liệu và trả về kết quả đã chuyển đổi.Triển khai ba kiểu bộ chuyển đổi:
PDFConverter: phương thứcConverttrả vềPDF: [content]HTMLConverter: phương thứcConverttrả vềHTML: <p>[content]</p>MarkdownConverter: phương thứcConverttrả vềMD: # [content]
Tạo hàm factory
NewConverter(format string) Convertertrả về bộ chuyển đổi phù hợp dựa trên chuỗi định dạng (pdf,htmlhoặcmarkdown). Với mọi định dạng không được nhận diện, hãy trả vềPDFConverterlàm mặc định.main.go: Dùng factory để tạo bộ chuyển đổi và xử lý tài liệu.Đọc loại định dạng và nội dung tài liệu. Dùng factory để tạo bộ chuyển đổi phù hợp, sau đó chuyển đổi nội dung và in kết quả ra.
Chương trình sẽ nhận các đầu vào sau:
- Dòng 1: Loại định dạng (
pdf,html,markdownhoặc định dạng khác) - Dòng 2: Nội dung tài liệu cần chuyển đổi
Ví dụ, với đầu vào:
html
Welcome to Go ProgrammingKết quả đầu ra sẽ là:
HTML: <p>Welcome to Go Programming</p>Và với đầu vào:
markdown
Chapter OneKết quả đầu ra sẽ là:
MD: # Chapter OneVà với đầu vào:
pdf
Annual Report 2024Kết quả đầu ra sẽ là:
PDF: Annual Report 2024Và với đầu vào:
docx
Meeting NotesKết quả đầu ra sẽ là:
PDF: Meeting NotesLưu ý rằng code chính không cần biết các kiểu bộ chuyển đổi cụ thể. Code chỉ yêu cầu factory cung cấp một bộ chuyển đổi rồi dùng giao diện. Nhờ vậy, bạn có thể dễ dàng thêm định dạng mới sau này mà không cần thay đổi code phía máy khách!
Tự làm thử
package main
import (
"bufio"
"fmt"
"os"
)
func main() {
scanner := bufio.NewScanner(os.Stdin)
// Đọc loại định dạng
scanner.Scan()
format := scanner.Text()
// Đọc nội dung tài liệu
scanner.Scan()
content := scanner.Text()
// TODO: Dùng factory để tạo bộ chuyển đổi phù hợp
// TODO: Chuyển đổi nội dung và in ra kết quả
fmt.Println(result)
}
Bài học này có một bài trắc nghiệm ngắn. Bắt đầu bài học để trả lời và theo dõi tiến độ.
Tất cả bài học trong Lập trình hướng đối tượng
1Nền tảng OOP trong Go
Tệp bên ngoàiWorkspace và module trong GoPackage và importTên được export và không được exportGiới thiệu về OOP trong GoStruct như lớpĐịnh nghĩa phương thức cho structReceiver kiểu con trỏ và kiểu giá trịKhởi tạo structHàm khởi tạoÔn tập: máy tính đơn giản4Interface
Giới thiệu về interfaceTriển khai ngầm địnhInterface như một hợp đồngInterface rỗng (any)Khẳng định kiểuswitch kiểuKết hợp interfaceInterface Stringer và ErrorÔn tập: Máy tính hình học7Tính đóng gói
Trường được xuất và không được xuấtĐóng gói ở cấp packagePhương thức getter và setterẨn thông tin trong GoÔn tập: hồ sơ học sinh10Generics (Go 1.18 trở lên)
Giới thiệu về GenericsTham số kiểuRàng buộc kiểuStruct tổng quátGiải pháp thay thế cho phương thức tổng quátÔn tập: tập hợp tổng quát13Các mẫu thiết kế phần 1
Giới thiệu về mẫu thiết kếMẫu SingletonMẫu FactoryMẫu Abstract FactoryMẫu ObserverMẫu Strategy2Tìm hiểu chuyên sâu về kiểu dữ liệu và struct
Kiểu cơ bản và kiểu tổng hợpĐịnh nghĩa kiểu dữ liệu tùy chỉnhThẻ structStruct ẩn danhStruct lồng nhauGiá trị zero và giá trị mặc địnhÔn tập: sổ liên lạc5Ưu tiên kết hợp hơn kế thừa
Vì sao Go không có kế thừaNhập môn nhúng structTự động nâng phương thứcNhúng nhiều structNhúng và kết tậpChe khuất phương thức được nhúngÔn tập: hệ phân cấp nhân viên8Xử lý lỗi và lập trình hướng đối tượng
Giao diện errorCác kiểu lỗi tùy chỉnhBọc lỗi với fmt.ErrorfLỗi sentinelerrors.Is() và errors.As()Panic, defer và recoverÔn tập: trình phân tích tệp3Con trỏ và bộ nhớ
Cơ bản về con trỏ trong GoCon trỏ đến structTruyền theo giá trị và tham chiếuHàm new()Thu gom rác trong GoÔn tập: xây dựng danh sách liên kết6Đa hình trong Go
Đa hình qua interfaceĐịnh kiểu vịt trong GoQuy tắc thỏa mãn interfaceCác tập hợp đa hìnhTiêm phụ thuộcÔn tập: bộ xử lý thanh toán9Lập trình đồng thời và lập trình hướng đối tượng
Cơ bản về goroutineChannel và giao tiếpChannel có bộ đệm và không có bộ đệmCâu lệnh selectsync.Mutex và sync.RWMutexsync.WaitGroupThiết kế struct an toàn luồngÔn tập: worker poolTự luyện tập: Trình biên dịch Go online