Mẫu Abstract 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 88 trên 107.
Mẫu Abstract Factory mở rộng mẫu Factory bằng cách tạo ra các nhóm đối tượng có liên quan mà không chỉ định kiểu cụ thể của chúng. Thay vì dùng một hàm factory duy nhất, bạn định nghĩa một giao diện factory để nhiều factory cụ thể triển khai, mỗi factory tạo ra một bộ đối tượng có liên quan nhất quán.
Hãy xem xét một bộ công cụ giao diện người dùng cần tạo nút và hộp kiểm. Mỗi hệ điều hành cần cách triển khai khác nhau, nhưng các thành phần phải hoạt động nhất quán với nhau:
type Button interface {
Render() string
}
type Checkbox interface {
Check() string
}
// Giao diện nhà máy trừu tượng
type GUIFactory interface {
CreateButton() Button
CreateCheckbox() Checkbox
}Mỗi nhà máy cụ thể tạo ra một họ các thành phần có liên quan:
// Họ Windows
type WindowsButton struct{}
func (w WindowsButton) Render() string { return "Windows Button" }
type WindowsCheckbox struct{}
func (w WindowsCheckbox) Check() string { return "Windows Checkbox" }
type WindowsFactory struct{}
func (w WindowsFactory) CreateButton() Button { return WindowsButton{} }
func (w WindowsFactory) CreateCheckbox() Checkbox { return WindowsCheckbox{} }
// Họ Mac
type MacButton struct{}
func (m MacButton) Render() string { return "Mac Button" }
type MacCheckbox struct{}
func (m MacCheckbox) Check() string { return "Mac Checkbox" }
type MacFactory struct{}
func (m MacFactory) CreateButton() Button { return MacButton{} }
func (m MacFactory) CreateCheckbox() Checkbox { return MacCheckbox{} }Code phía máy khách làm việc với giao diện factory và không phụ thuộc vào các cách triển khai cụ thể:
func BuildUI(factory GUIFactory) {
button := factory.CreateButton()
checkbox := factory.CreateCheckbox()
fmt.Println(button.Render(), checkbox.Check())
}Hãy dùng Abstract Factory khi hệ thống của bạn cần làm việc với nhiều họ sản phẩm liên quan và bạn muốn đảm bảo các thành phần thuộc cùng một họ được dùng cùng nhau. Mẫu này phức tạp hơn Factory đơn giản, vì vậy chỉ dùng khi bạn thực sự cần quản lý các họ sản phẩm.
Thử thách
DễHãy xây dựng hệ thống cửa hàng nội thất bằng mẫu Abstract Factory! Bạn sẽ tạo các nhà máy sản xuất những nhóm đồ nội thất có liên quan: mỗi nhà máy tạo ra một phong cách nhất quán (Modern hoặc Vintage), đảm bảo ghế và bàn từ cùng một nhà máy có vẻ ngoài hài hòa.
Bạn sẽ sắp xếp code trong ba tệp:
furniture.go: Định nghĩa các giao diện sản phẩm và các kiểu đồ nội thất cụ thể.Tạo hai giao diện sản phẩm:
Chairvới phương thứcSitOn() stringTablevới phương thứcPlaceItems() string
Triển khai hai nhóm đồ nội thất:
- Nhóm Modern:
ModernChairtrả vềSitting on a sleek modern chairvàModernTabletrả vềPlacing items on a glass modern table - Nhóm Vintage:
VintageChairtrả vềSitting on an ornate vintage chairvàVintageTabletrả vềPlacing items on a wooden vintage table
factory.go: Định nghĩa giao diện nhà máy trừu tượng và các nhà máy cụ thể.Tạo giao diện
FurnitureFactoryvới hai phương thức:CreateChair() ChairCreateTable() Table
Triển khai hai nhà máy cụ thể:
ModernFactorytạo raModernChairvàModernTableVintageFactorytạo raVintageChairvàVintageTable
Đồng thời, tạo hàm
GetFactory(style string) FurnitureFactorytrả về nhà máy phù hợp dựa trên phong cách (modernhoặcvintage). Mặc định trả vềModernFactorynếu không nhận diện được phong cách.main.go: Dùng các nhà máy để trang bị nội thất cho phòng.Tạo hàm
FurnishRoom(factory FurnitureFactory) stringdùng nhà máy để tạo một chiếc ghế và một chiếc bàn, sau đó trả về chuỗi đã định dạng, hiển thị hai hành động trên các dòng riêng biệt.Đọc đầu vào về phong cách, lấy nhà máy phù hợp, trang bị nội thất cho phòng và in ra kết quả.
Các đầu vào sau sẽ được cung cấp:
- Dòng 1: Phong cách nội thất (
modern,vintagehoặc một giá trị khác)
Ví dụ, với đầu vào:
modernKết quả đầu ra sẽ là:
Sitting on a sleek modern chair
Placing items on a glass modern tableVới đầu vào:
vintageKết quả đầu ra sẽ là:
Sitting on an ornate vintage chair
Placing items on a wooden vintage tableVới đầu vào:
rusticKết quả đầu ra sẽ là:
Sitting on a sleek modern chair
Placing items on a glass modern tableLưu ý rằng hàm FurnishRoom có thể làm việc với mọi nhà máy thông qua giao diện. Hàm không biết mình đang tạo đồ nội thất Modern hay Vintage. Nhờ vậy, mọi món đồ nội thất trong phòng đều đến từ cùng một nhóm, giữ cho phong cách nhất quán!
Tự làm thử
package main
import "fmt"
// FurnishRoom dùng bộ tạo để tạo đồ nội thất và trả về kết quả
// TODO: Triển khai hàm (function) này
// Dùng bộ tạo để tạo một chiếc ghế và một chiếc bàn
// Trả về chuỗi có kết quả SitOn() ở dòng đầu và kết quả PlaceItems() ở dòng thứ hai
func FurnishRoom(factory FurnitureFactory) string {
// TODO: Dùng bộ tạo để tạo ghế và bàn
// TODO: Trả về chuỗi đã định dạng, mỗi hành động trên một dòng riêng
return ""
}
func main() {
var style string
fmt.Scanln(&style)
// TODO: Chọn bộ tạo phù hợp dựa trên style
// TODO: Dùng bộ tạo để trang bị đồ nội thất cho một căn phòng
// TODO: In ra kết quả
}
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