Универсальная функция ограничения путем сравнения типа свойства экземпляра с универсальным параметром - PullRequest
2 голосов
/ 05 марта 2019

Я хочу создать подкласс ViewControllers для создания общего класса Coordinator. Этот класс-координатор должен иметь возможность безопасно вводить зависимости для дочерних классов-координаторов. Дочерние координаторы должны иметь доступ только к явно определенным зависимостям. Я сделал следующую рабочую площадку с абстрактными классами для решения проблемы. Я открыт для других идей, как решить описанную проблему.

Предпосылки

import Foundation

protocol HasFirstDependency {
    var first: Any? { get }
}

protocol HasSecondDependency {
    var second: Any? { get }
}

typealias AllDependencies = HasFirstDependency & HasSecondDependency

struct AppDependencies: AllDependencies {
    var first: Any?
    var second: Any?
}



class Coordinator<D> {
    var dependencies: D?
}

extension Coordinator {

    static func initialize() -> Coordinator<D> {
        return Coordinator<D>()
    }

}

class ParentCoordinator: Coordinator<AllDependencies> {
    var children: [Any] = []
}

class FirstChildCoordinator: Coordinator<HasFirstDependency> {}

class SecondChildCoordinator: Coordinator<HasSecondDependency> {}

Вопрос

Следующий код обрисовывает в общих чертах вопрос (см. Комментарии 1. и 2.). Есть ли способ избежать приведения и ограничения childType во время компиляции описанным способом?

extension ParentCoordinator {

    func add<C: Coordinator<D>, D>(childType: C.Type) { // 2. ... by setting a constraint like this: "where self.dependecies is D?"
        let child = C.initialize()
        children.append(child)
        if let dependencies: D? = self.dependencies as? D? { // 1. is there any way to avoid this casting ...
            child.dependencies = dependencies
        } else {
            assertionFailure("parentCoordinator does not hold child dependencies")
        }
    }

}

let parent = ParentCoordinator()
parent.dependencies = AppDependencies(first: "bla", second: "blup")
parent.add(childType: FirstChildCoordinator.self)
let child = parent.children.first as? Coordinator<HasFirstDependency>
print(type(of: parent.dependencies)) // Optional<HasFirstDependency & HasSecondDependency>
print(parent.dependencies?.first) // Optional("bla")
print(parent.dependencies?.second) // Optional("blup")
print(type(of: child?.dependencies)) // Optional<HasFirstDependency>
print(child?.dependencies?.first) // Optional("bla")
//print(child?.dependencies?.second) // does not compile, what I wanted to achieve

Что я хочу

Чтобы быть более конкретным: следующая фатальная ошибка, которую я хочу отловить во время компиляции.

protocol ForgottenDependency {
    var forgotten: Any? { get }
}

class ThirdChildCoordinator: Coordinator<ForgottenDependency> {}

parent.add(childType: ThirdChildCoordinator.self) // Fatal error: parentCoordinator does not hold child dependencies: file MyPlayground.playground, line 49

1 Ответ

1 голос
/ 06 марта 2019

То, что вы запрашиваете, является своего рода логическим OR для ограничений типа:

... where D == HasFirstDependency || HasSecondDependency

Разъединение ограничений типа невозможно в swift. Чтобы процитировать обычно отклоненные изменения :

Разъединения (логические ИЛИ) в ограничениях типов: они включают в себя анонимные типы, подобные объединению (например, (Int | String) для типа, который может быть заселен как целым числом, так и строкой). «[Этот тип ограничений] является чем-то, что система типов не может и не должна поддерживать.»

Одним из возможных решений может быть приведение зависимостей в соответствие с общим протоколом. Это, честно говоря, не совсем решает проблему обнаружения «забытых» зависимостей, так как все зависимости должны будут реализовывать этот общий протокол. Этот подход обсуждался здесь .

...