Konstrukcja na podstawie przekazanego typu…

Popatrzmy na konstrukcję obiektów w Swift trochę odwrotnie. Jeśli znacie narzędzi JSONDecoder(), to na pewno widzieliście wywołanie typu

let decoder = JSONDecoder()
let product = try decoder.decode(A.self, from: json)

Na podstawie typu przekazanego jako parametr,  funkcja decode konstruuje instancję obiektu/struktury typu A. jednak nie widać tu bezpośredniego wywołania konstruktora typu.

let a = A()

Jak więc jest to zrealizowane? Spróbujmy napisać własny przykład, w którym stworzymy funkcję fabrykującą obiekty pewnego typu.

W funkcji decode z klasy JSONDecoder wymagane jest, aby podany typ był zgodny z protokołem Codable. Zrobimy coś podobnego. Potrzebujemy protokołu, który pozwoli zagwarantować, że wykorzystane typy będą pozwalały na konstrukcję ich obiektów. Protokół Initializable bedzie więc wymagać istnienia funkcji init().

protocol Initializable {
    init()
}

Mając tak zdefiniowany protokoł możemy przejść do implementacji funkcji fabrykującej. Skorzystamy z notacji <> pozwalającej na stworzenie funkcji generycznej.

func createInstance<T>(typeThing:T.Type) -> T where T:Initializable{
    return typeThing.init()
}

Funkcja createInstance jest więc sparametryzowana typem <T>, który jest dedukowany z przekazanego parametru (T.Type). Zwracany jest obiekt typu T. Na końcu nagłówka funkcji możemy zobaczyć, że typ T musi implementować nasz protokoł (where T:Initializable).

Dzięki temu jesteśmy pewni, że typ implementuje funkcję init(), a zatem poprawna będzie instrukcja  return typeThing.init(). W przypadku metatypów nie można użyć zwykłej notacji typeThing() tylko trzeba jawnie określić wywołanie konkretnego konstruktora.

Konstrukcja obiektu dowolnego typu spełniającego określone warunki będzie więc wymagała użycia:

let a = createInstance(typeThing: SomeTestType.self)

Oczywiście pozostał jeszcze do zdefiniowania sam typ SomeTestType, który będąc zgodnym z protokołem Initializable musi implementować konstruktor init().

class SomeTestType : Initializable {
    var property:String
    required init() {
        property = "It works!"
    }

}

Gotowe.

Dodaj komentarz

Twój adres email nie zostanie opublikowany. Pola, których wypełnienie jest wymagane, są oznaczone symbolem *