Jest jeszcze jedno rozwiązanie, które nie zostało wspomniane w tym pytaniu, a mianowicie użycie techniki zwanej wymazywaniem typów . Aby uzyskać abstrakcyjny interfejs dla protokołu ogólnego, utwórz klasę lub strukturę, która otacza obiekt lub strukturę zgodną z protokołem. Klasa opakowania, zwykle nazywana „Any {nazwa protokołu}”, sama jest zgodna z protokołem i implementuje swoje funkcje, przekazując wszystkie wywołania do obiektu wewnętrznego. Wypróbuj poniższy przykład na placu zabaw:
import Foundation
public protocol Printer {
typealias T
func print(val:T)
}
struct AnyPrinter<U>: Printer {
typealias T = U
private let _print: U -> ()
init<Base: Printer where Base.T == U>(base : Base) {
_print = base.print
}
func print(val: T) {
_print(val)
}
}
struct NSLogger<U>: Printer {
typealias T = U
func print(val: T) {
NSLog("\(val)")
}
}
let nsLogger = NSLogger<Int>()
let printer = AnyPrinter(base: nsLogger)
printer.print(5)
Typ printerjest znany AnyPrinter<Int>i można go użyć do abstrakcji dowolnej możliwej implementacji protokołu drukarki. Chociaż AnyPrinter nie jest technicznie abstrakcyjna, jego implementacja jest tylko przejściem do prawdziwego typu implementującego i może być używana do oddzielania typów implementujących od typów, które ich używają.
Należy zwrócić uwagę na to, że AnyPrinternie musi jawnie zachowywać instancji podstawowej. W rzeczywistości nie możemy, ponieważ nie możemy zadeklarować AnyPrinterposiadania Printer<T>właściwości. Zamiast tego otrzymujemy wskaźnik funkcji do funkcji _printbazy print. Wywołanie base.printbez wywołania zwraca funkcję, w której baza jest pobierana jako zmienna self, a zatem jest zachowywana dla przyszłych wywołań.
Inną rzeczą, o której należy pamiętać, jest to, że to rozwiązanie jest zasadniczo kolejną warstwą dynamicznej wysyłki, co oznacza niewielki spadek wydajności. Ponadto instancja wymazywania typu wymaga dodatkowej pamięci w górnej części podstawowej instancji. Z tych powodów wymazywanie typów nie jest bezpłatną abstrakcją.
Oczywiście jest trochę pracy, aby ustawić wymazywanie typów, ale może być bardzo przydatne, jeśli potrzebna jest abstrakcyjna abstrakcja protokołu. Ten wzorzec znajduje się w szybkiej bibliotece standardowej z typami takimi jak AnySequence. Dalsza lektura: http://robnapier.net/erasure
PREMIA:
Jeśli zdecydujesz, że chcesz wstrzyknąć tę samą implementację Printerwszędzie, możesz zapewnić wygodny inicjator, AnyPrinterktóry wstrzykuje ten typ.
extension AnyPrinter {
convenience init() {
let nsLogger = NSLogger<T>()
self.init(base: nsLogger)
}
}
let printer = AnyPrinter<Int>()
printer.print(10)
Może to być łatwy i SUCHY sposób wyrażania iniekcji zależności dla protokołów używanych w całej aplikacji.