Menu
CoddyTech

Mẫu Factory

Thuộc phần Lập trình hướng đối tượng trong hành trình Swift của Coddy. Bài học 43 trên 57.

Mẫu factory gom việc tạo đối tượng vào một nơi. Bên gọi yêu cầu thứ mình cần, thường theo tên hoặc kiểu, rồi nhận về một đối tượng sẵn sàng sử dụng mà không cần biết kiểu cụ thể nào đã được tạo:

protocol Shape { func area() -> Double }
struct Square: Shape { let side: Double; func area() -> Double { side * side } }
struct Circle: Shape { let r: Double; func area() -> Double { 3 * r * r } }

enum ShapeFactory {
    static func make(_ kind: String, size: Double) -> (any Shape)? {
        switch kind {
        case "square": return Square(side: size)
        case "circle": return Circle(r: size)
        default: return nil
        }
    }
}

print(ShapeFactory.make("square", size: 3)!.area())
print(ShapeFactory.make("hexagon", size: 3) == nil)

Đầu ra:

9.0
true

Bên gọi chỉ phụ thuộc vào giao thức. Khi thêm một hình dạng mới, bạn chỉ cần thay đổi factory, không phải mọi nơi tạo hình dạng:

protocol Notifier { func send(_ m: String) -> String }
struct Email: Notifier { func send(_ m: String) -> String { "email: \(m)" } }
struct SMS: Notifier { func send(_ m: String) -> String { "sms: \(m)" } }

func makeNotifier(for user: String) -> any Notifier {
    user.hasSuffix("@mail.com") ? Email() : SMS()
}

print(makeNotifier(for: "ada@mail.com").send("hi"))
print(makeNotifier(for: "555-0100").send("hi"))

Đầu ra:

email: hi
sms: hi

Phương thức factory tĩnh trên chính kiểu dữ liệu đó là một dạng nhẹ hơn của mẫu này. Chúng đặt tên cho những cách phổ biến để tạo giá trị:

struct Color {
    let r: Int, g: Int, b: Int
    static func gray(_ v: Int) -> Color { Color(r: v, g: v, b: v) }
    static let white = Color(r: 255, g: 255, b: 255)
}

print(Color.gray(100).g)
print(Color.white.b)

Đầu ra:

100
255

Một enum không có trường hợp nào, chẳng hạn như ShapeFactory, là cách phổ biến để nhóm các hàm tạo: enum này không thể được khởi tạo, nên chỉ hoạt động như một không gian tên.

enum ShapeFactory {        // không có trường hợp nào: không thể tạo instance
    static func make(_ kind: String, size: Double) -> (any Shape)? { ... }
}
challenge icon

Thử thách

Dễ

Viết một factory cho các gói giá. Protocol Plan có name và price(forUsers:) -> Int. Triển khai Free (0, và chỉ áp dụng tối đa 3 người dùng: vượt quá mức đó thì giá là -1, nghĩa là không được phép), Team (10 cho mỗi người dùng) và Enterprise (giá cố định 500 cộng thêm 5 cho mỗi người dùng vượt quá 100). Hoàn thiện PlanFactory.make(_:), hàm này trả về gói tương ứng với free, team hoặc enterprise, và trả về nil trong các trường hợp khác.

Code được cung cấp đọc các dòng plan,users và in ra team for 12 users: 120, free does not allow 5 users hoặc unknown plan: gold.

Đặt code của bạn trong các tệp Plan.swift, Free.swift, Team.swift, Enterprise.swift và PlanFactory.swift. main.swift chứa code đầu vào/đầu ra được cung cấp và không thể chỉnh sửa.

Tự làm thử

// Đoạn code đầu vào/đầu ra đã được cung cấp: giữ nguyên
var input: [String] = []
while let line = readLine() { input.append(line) }
for line in input {
    let p = line.split(separator: ",").map(String.init)
    let users = Int(p[1])!
    guard let plan = PlanFactory.make(p[0]) else {
        print("unknown plan: \(p[0])")
        continue
    }
    let price = plan.price(forUsers: users)
    print(price < 0 ? "\(plan.name) does not allow \(users) users" : "\(plan.name) for \(users) users: \(price)")
}
quiz iconTự kiểm tra

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

Tự luyện tập: Playground Swift