Протоколы со связанным типом и фабричным образцом? - PullRequest
0 голосов
/ 11 ноября 2018

Мой уровень доступа к данным состоит из общего Repository протокола

protocol Repository {
    associatedtype T
    func getAll() -> Promise<[T]>
}

и его конкретная реализация:

class FirebaseRepository<T: Model>: Repository {
    func getAll() -> Promise<[T]> {
        fatalError()
    }
}

В основном, Repository может быть RestRepository, FirebaseRepository, PlistRepositry и т. Д. Хранилище данных используется бизнес-логикой:

/// My business logic
class ModelService<T: Repository> {
    private let repository: T

    public init(repository: T) {
        self.repository = repository
    }
}

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

/// Returns a concrete Repository implementation
class RepositoryFactory {
    func makeRepository<T: Model>(type: T.Type) -> Repository {
        return FirebaseRepository<T>()
    }
}

и это определенно вызывает ошибку компилятора:

Protocol 'Repository' can only be used as a generic constraint because it has Self or associated type requirements

Единственный жизнеспособный вариант, к которому я пришел, это:

func makeRepository<T: Model>(type: T.Type, U: Repository) -> U {
    return FirebaseRepository<T>() as! U
}

но, как вы понимаете, принудительное распаковывание не допускается в производственном коде.

Как заставить протоколы со связанными типами работать с шаблоном фабричного проектирования?

1 Ответ

0 голосов
/ 11 ноября 2018

Вы можете использовать Тип стирания . Вот пример:

protocol CustomProtocol {
    associatedtype AssociatedType
    func foo(argument: AssociatedType)
    func bar() -> AssociatedType
}

Если вы хотите использовать CustomProtocol напрямую, вы получите ошибку:

let array = [CustomProtocol]()

Протокол 'CustomProtocol' может использоваться только в качестве общего ограничения, поскольку он имеет требования к Self или связанный тип

Таким образом, вы можете сделать тот же трюк, что и Свифт с их последовательностями:

public struct AnyCustomProtocol<T>: CustomProtocol {
    func bar() -> T {
        fatalError("Needs implementation")
    }
    func foo(argument: T) {

    }
}

let array = [AnyCustomProtocol<Any>]() // works fine

Ваше решение проблемы в этом случае будет выглядеть примерно так:

    class Promise<T> {

    }

    protocol Model {

    }

    protocol Repository {
        associatedtype T
        func getAll() -> Promise<[T]>
    }

    class FirebaseRepository<T: Model>: AnyRepository<T> {
        override func getAll() -> Promise<[T]> {
            fatalError()
        }
    }

    class AnyRepository<T>: Repository {
        func getAll() -> Promise<[T]> {
            fatalError()
        }
    }


    class RepositoryFactory {
        func makeRepository<T: Model>(type: T.Type) -> AnyRepository<T> {
            return FirebaseRepository<T>()
        }
    }

__

Для дальнейшего чтения вы можете проверить это и Официальные документы по Generics

...