Menu
Coddy logo textTech

Fabrikmuster

Teil des Abschnitts Objektorientierte Programmierung der Swift-Journey von Coddy. Lektion 43 von 57.

Das Fabrikmuster bündelt die Objekterstellung an einer Stelle. Aufrufer fragen nach dem, was sie benötigen, oft anhand eines Namens oder Typs, und erhalten ein fertiges Objekt, ohne zu wissen, welcher konkrete Typ erstellt wurde:

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)

Ausgabe:

9.0
true

Der Aufrufer hängt nur vom Protokoll ab. Das Hinzufügen einer neuen Form bedeutet, die Factory zu ändern, nicht jede Stelle, an der Formen erstellt werden:

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"))

Ausgabe:

email: hi
sms: hi

Statische Factory-Methoden am Typ selbst sind eine leichtere Form des Musters. Sie geben gängigen Möglichkeiten zum Erstellen eines Werts Namen:

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)

Ausgabe:

100
255

Ein Enum ohne Fälle, wie etwa ShapeFactory, ist eine gängige Möglichkeit, Factory-Funktionen zu gruppieren: Es kann nicht instanziiert werden und fungiert daher ausschließlich als Namespace.

enum ShapeFactory {        // keine cases: kann nicht erstellt werden
    static func make(_ kind: String, size: Double) -> (any Shape)? { ... }
}
challenge icon

Aufgabe

Einfach

Schreibe eine Fabrik für Preispläne. Das Protokoll Plan hat name und price(forUsers:) -> Int. Implementiere Free (0 und nur bis zu 3 Benutzer: darüber beträgt der Preis -1, was „nicht erlaubt“ bedeutet), Team (10 pro Benutzer) und Enterprise (500 pauschal plus 5 pro Benutzer über 100). Vervollständige PlanFactory.make(_:), das den Plan für free, team oder enterprise zurückgibt und andernfalls nil.

Der bereitgestellte Code liest Zeilen im Format plan,users ein und gibt team for 12 users: 120, free does not allow 5 users oder unknown plan: gold aus.

Dein Code gehört in Plan.swift, Free.swift, Team.swift, Enterprise.swift und PlanFactory.swift. main.swift enthält den bereitgestellten Ein-/Ausgabecode und kann nicht bearbeitet werden.

Probier es selbst

// Bereitgestellter Ein-/Ausgabe-Code: belasse ihn wie er ist
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 iconTeste dich selbst

Diese Lektion enthält ein kurzes Quiz. Starte die Lektion, um es zu beantworten und deinen Fortschritt zu speichern.

Alle Lektionen in Objektorientierte Programmierung

Übe selbstständig: Swift-Playground