Menu
Coddy logo textTech

Pattern Factory

Fa parte della sezione Programmazione orientata agli oggetti del percorso Swift di Coddy. Lezione 43 di 57.

Il pattern factory centralizza la creazione degli oggetti. Chi chiama richiede ciò di cui ha bisogno, spesso tramite nome o tipo, e riceve un oggetto pronto, senza sapere quale tipo concreto è stato creato:

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)

Output:

9.0
true

Il chiamante dipende solo dal protocollo. Aggiungere una nuova forma significa modificare la factory, non ogni punto in cui vengono create le forme:

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

Output:

email: hi
sms: hi

I metodi factory statici sul tipo stesso sono una forma più leggera del pattern. Danno un nome ai modi comuni di creare un valore:

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)

Output:

100
255

Un enum senza casi, come ShapeFactory, è un modo comune per raggruppare funzioni factory: non può essere istanziato, quindi funziona esclusivamente come namespace.

enum ShapeFactory {        // nessun caso: non può essere creato
    static func make(_ kind: String, size: Double) -> (any Shape)? { ... }
}
challenge icon

Sfida

Facile

Scrivi una factory per i piani tariffari. Il protocollo Plan ha name e price(forUsers:) -> Int. Implementa Free (0, e al massimo 3 utenti: oltre tale soglia il prezzo è -1, ovvero non consentito), Team (10 per utente) ed Enterprise (500 fissi più 5 per ogni utente oltre 100). Completa PlanFactory.make(_:), che restituisce il piano per free, team o enterprise e nil negli altri casi.

Il codice fornito legge righe plan,users e stampa team for 12 users: 120, free does not allow 5 users o unknown plan: gold.

Il tuo codice va in Plan.swift, Free.swift, Team.swift, Enterprise.swift e PlanFactory.swift. main.swift contiene il codice di input/output fornito e non può essere modificato.

Provalo tu

// Codice di input/output fornito: lascialo così com'è
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 iconMettiti alla prova

Questa lezione include un breve quiz. Inizia la lezione per rispondere e tenere traccia dei tuoi progressi.

Tutte le lezioni di Programmazione orientata agli oggetti

Esercitati da solo: Playground Swift