2015-05-25 30 views
5

Tworzę strukturę jednostek w Swift, która ma nadklasę i podklasy pomiarowe dla różnych jednostek, takich jak masa i objętość. Cechą jest umożliwienie ramie prawidłowego odgadnięcia, z której jednostki jest tworzona i zwrócenia poprawnej klasy. przykładowy kod:Czy inicjalizator dla nadklasy zwraca konkretną podklasę?

class Measurement { 
    var unitString : String 
    init(unknownUnit: String) { 
     // checks if the unit is either Volume or Mass, and returns an instance of that class 
    } 
} 

class Volume : Measurement { 
    init(unitString: String) { 

    } 
} 

class Mass : Measurement { 
    init(unitString: String) { 

    } 
} 

let mass = Mass("kg")     // class: Mass 
let volume = Volume("ml")    // class: Volume 
let shouldBeVolume = Measurement("ml") // class: Volume 
let shouldBeMass = Measurement("kg") // class: Mass 

Czy jest możliwe, aby dziedziczona klasa tworzyła obiekt określonej podklasy podczas jej inicjowania?

Biblioteka nazywa Indus Valley i open source na GitHub

+0

Jest to z pewnością denerwujące ograniczenie Swift 'nie init' powrocie wartość. – pkamb

Odpowiedz

0

To gra szybko i luźne z dziedziczenia posiadające klasę rodzic wie o jej podklas (bardzo słabej anty-wzór!), Ale to będzie działać ...

class Measurement { 
    var unitString : String 

    class func factory(unknownUnit: String) -> Measurement { 
     if unknownUnit == "kg" { 
      return Mass(myUnit: unknownUnit) 
     } else { // Random default, or make func return Measurement? to trap 
      return Volume(myUnit: unknownUnit) 
     } 
    } 

    init(myUnit: String) { 
     // checks if the unit is either Volume or Mass, and returns an instance of that class 
     self.unitString = myUnit 
    } 
} 

class Volume : Measurement { 
} 

class Mass : Measurement { 
} 

let mass = Mass(myUnit: "kg")     // class: Mass 
let volume = Volume(myUnit: "ml")    // class: Volume 
let shouldntBeVolume = Measurement(myUnit: "ml") // class: Measurement 
let shouldntBeMass = Measurement(myUnit: "kg") // class: Measurement 
let isVolume = Measurement.factory("ml") // class: Volume 
let shouldBeMass = Measurement.factory("kg") // class: Mass 
1

Jeśli tworzysz obiekt podklasy w superklasie, aplikacja zostanie zawieszona, ponieważ wywołanie metody init będzie rekursywne. Aby przetestować, możesz po prostu utworzyć hierarchię klasy i spróbować utworzyć obiekt podklasy w super klasie.

Możesz rozwiązać ten problem, korzystając z zaprojektowanego wzoru. Wystarczy utworzyć jedną klasę interfejsu, która wewnętrznie używa wszystkich innych klas i utworzyć obiekt i zwrócić.

class UnitConverter { 
    class func measurement(unknownUnit: String) -> Measurement { 
     if unknownUnit == "kg" { 
      return Mass(unknownUnit) 
     } else if unknownUnit == "ml" { 
      return Volume(unknownUnit) 
     } 

     return Measurement(unknownUnit) 
    } 
} 
+0

Jest to prawdą tylko wtedy, gdy podklasa wywołuje * ten sam * inicjator rodzica. Jednak jeśli podklasy wywołają specjalny (na przykład prywatny) inicjator superklasy, nie będzie on w nieskończoność rekurencyjny. Tak to działało w Obj-C. – devios1

1

W zależności od przypadku zastosowania, może to może być odpowiednią alternatywą:

enum Measurement { 
    case Mass 
    case Volume 

    static func fromUnit(unit:String) -> Measurement? { 
     switch unit { 
      case "mg", "g", "kg": return Mass 
      case "ml", "dl", "l": return Volume 

      default: return nil 
     } 
    } 
} 
+0

Tak, to jest moje obecne rozwiązanie, zastanawiałem się, czy ktokolwiek miał dla inicjatorów :) – hakonbogen