2016-05-31 17 views
25

Sono un principiante di Swift e sto cercando di capire come usare Structs con proprietà facoltative. Ho fatto un bel po 'di ricerche e ho ottenuto qualcosa che funziona ma si sente incredibilmente inefficiente, quindi mi chiedevo se ci fosse un modo migliore/più gestibile per raggiungere il mio obiettivo.Iniziale Swift Struct con proprietà memorizzate opzionali

Mi piacerebbe utilizzare Structs per rappresentare un'azienda, ma non ho idea in anticipo di quale combinazione di proprietà è probabile che abbia un'azienda specifica. Questo sembra significare che devo creare un init() per ogni possibile combinazione di parametri.

Ecco un esempio semplificato (Ho molte più proprietà):

import Foundation 

struct Business { 
    let name : String 
    var web : String? 
    var address: String? 

    // just the business name 
    init(busName: String) { 
     self.name = busName 
    } 

    // business name + website 
    init(busName: String, website: String) { 
     self.name = busName 
     self.web = website 
    } 

    // business name + address 
    init(busName: String, address: String) { 
     self.name = busName 
     self.address = address 
    } 

    // business name + website + address 
    init(busName: String, website: String, address: String) { 
     self.name = busName 
     self.web = website 
     self.address = address 
    } 
} 

Posso quindi inizializzare il classe come questa:

Business(busName: "Dave's Cafe", website: "http://www.davescafe.com") 

Business(busName: "Sarah's Brewhouse", address: "41 Acacia Ave, Smalltown") 

Non c'è un modo per creare una sorta di init () dove i parametri sono opzionali? Se tu potessi indicarmi la direzione di termini o concetti per la ricerca, sarebbe fantastico.

risposta

36

usa i valori predefiniti:

init(busName: String, website: String? = nil, address: String? = nil) { 
    self.name = busName 
    self.web = website 
    self.address = address 
} 

quindi è possibile chiamare l'init come questo:

_ = Business(busName: "Foo") 
_ = Business(busName: "Foo", website: "www.foo.bar") 
_ = Business(busName: "Foo", address: "bar") 
_ = Business(busName: "Foo", website: "www.foo.bar", address: "bar") 
+1

Questi dovrebbero essere 'String?' E non 'String'. – jtbandes

+0

Grazie a @dasdom ho provato ad usare il '?' Dopo il tipo (es. '... website: String? ...' come pensavo che l'utilizzo di quella sintassi impostasse automaticamente il valore su nil. Questo non funzionava, quindi non ho provate a impostare esplicitamente il valore su zero. Proverò ora. – James

+0

Sì, 'String' o' String? 'è solo un tipo; non ha molto a che fare con un valore predefinito (purché stiamo parlando di parametri di funzione). Avresti potuto avere anche un valore di default per un parametro String, come 'init (busName: String =" ")' anche se penso che gli optionals siano il modo giusto per farlo. – jtbandes

9

Un approccio che si può prendere in prestito da altre lingue OOP è parametro builder. Inizia con un metodo statico che restituisce un costruttore, quindi aggiungere i metodi per i singoli parametri, e, infine, chiamare build():

let bakery = Business 
    .withName("Black Forest") 
    .andWebSite("www.blackforest.com") 
    .andAddress("1 Main St, Springfield, IA 98765") 
    .build() 

Ecco un'implementazione dello scheletro che consente questo tipo di un'API:

class Business { 
    // Users never call this init, it's for the builder to use 
    init(name: String, webSite: String?, address: String?) { 
     ... 
    } 
    // Here is the method the users call: 
    static func withName(name: String) { 
     return BusinessBuilder(name) 
    } 
    // This class collects parameters before calling init 
    class BusinessBuilder { 
     var name : String 
     var webSite : String? 
     var address: String? 
     func andAddress(address: String) -> BusinessBuilder { 
      self.address = address 
      return self 
     } 
     func andWebSite(webSite: String) -> BusinessBuilder { 
      self.webSite = webSite 
      return self 
     } 
     func build() -> Business { 
      return Business(name, webSite, address) 
     } 
     init(name: String) { 
      self.name = name 
     } 
    } 
} 

Questo ti consente di passare il numero di parametri di inizializzazione minimo o uguale a quello che ritieni opportuno, in qualsiasi ordine che trovi conveniente in una determinata situazione.

L'utilizzo principale di questo approccio è quando non si conoscono i parametri che si otterranno, ad esempio, quando provengono da un XML o da un database. È possibile chiamare i metodi andXyz in un ciclo e quindi chiamare build() quando non si dispone di altri attributi da impostare.

+0

Il builder sembra non necessario per Esempio semplice di OP, ma potrebbe essere utile se OP intende ridimensionare a più campi. – Alexander

+5

@AMomchilov C'è una barzelletta sul fatto che gli schemi di progettazione sono un modo per aggirare le funzionalità che mancano dal linguaggio di programmazione. Il modello di builder è stato introdotto in Java principalmente per compensare la mancanza della funzione dei parametri denominata dalla lingua. Swift, d'altra parte, affronta questo problema in modo nativo, quindi il motivo principale per usare builder in Swift è essere in grado di passare i parametri di inizializzazione uno alla volta. – dasblinkenlight

+0

Anche Java non disponeva di parametri predefiniti, quindi può costringervi a dover rompere il modello di builder per una classe con solo diversi parametri. – Alexander

Problemi correlati