Во-первых, стандартный совет:
Я пытаюсь реализовать универсальный шаблон Mediator в Swift
Не. Начните с фактической проблемы, которую вы пытаетесь решить, и спроектируйте хорошие и необходимые абстракции для этой проблемы. Не создавайте общие вещи, просто чтобы быть общими. Свифт будет кусать тебя снова и снова. Даже stdlib, который на самом деле нуждается в супер-универсальных вещах, часто должен выйти за пределы чистого Swift, чтобы осуществить его (используя специальные знания компилятора и шаблонные шаблоны). «Быть общим» не является самоцелью. Вы почти наверняка делаете это слишком сложным. Все делают.
ОК, это не так. Второй совет: это не очень хорошее использование протокола со связанными типами (PAT). Смысл PAT заключается в добавлении методов к типам, а не к be типам. Вы никогда не передадите себе Collection
и не будете хранить вещи такого «типа». Вы создаете методы, которые могут работать с любым типом, который является Collection
. Нет такого типа как [Collection]
.
Основная проблема вашего подхода заключается в том, что невозможно реализовать RequestProcessor.process()
, не прибегая к as?
приведению, что нарушает точку безопасности типов. Откуда processor
знает, как звонить LoginHandler.process
? Почему этот? Что, если два разных обработчика принимают LoginRequest
? Что если никакой обработчик не принимает этот тип?
То, что вы разработали здесь, не является шаблоном Посредника. Шаблон Mediator объединяет коллег, имеющих общий интерфейс, поэтому он будет выглядеть следующим образом:
class RequestProcessor<Request> {
var handlers: [(Request) -> Void] = []
func register(handler: @escaping (Request) -> Void) {
handlers.append(handler)
}
func handle(request: Request) {
for handler in handlers {
handler(request)
}
}
}
И у вас будет RequestProcessor
для каждого типа запроса, а не универсальный «обработчик каждого типа запроса». Создание универсального обязательно (в Swift) удаляет безопасность типов, и в этом случае вы в основном создаете слегка Swiftier NotificationCenter
. (Можно создать типобезопасную версию этого, но она требует зависимых типов, что является довольно сложной функцией типа, которой нет у Swift.)
ОК, может, вам действительно нужен этот центральный концентратор, и кому нужна безопасность типов? Почему бы и нет? Вы просто должны сказать, что вы имеете в виду, что любой обработчик должен иметь возможность принять любой запрос, даже если он не действует на него. Компилятор не может доказать ничего более конкретного, чем это, потому что во время компиляции он не знает типов. Так хорошо, as?
это до смерти.
protocol Request {}
protocol Handler {
func canHandle(_ request: Request) -> Bool
func handle(_ request: Request)
}
class RequestProcessor {
private var handlers: [Handler] = []
func register(_ handler: Handler) {
handlers.append(handler)
}
func handle(_ request: Request) {
for handler in handlers where handler.canHandle(request) {
handler.handle(request)
}
}
}
class LoginHandler: Handler {
func canHandle(_ request: Request) -> Bool {
return request is LoginRequest
}
func handle(_ request: Request) {
guard let loginRequest = request as? LoginRequest else { return }
// handle loginRequest
}
}
Но я почти наверняка избавлюсь от схемы Посредника. Если цель состоит в том, чтобы загружать и выключать процессоры для тестирования или чего-то еще, я бы просто использовал типичные методы внедрения зависимостей. Передайте LoginHandler
любому методу, создающему LoginRequest
.