Как выставить разные функциональные возможности одного типа для разных модулей?
Я работаю над небольшим приложением для двух игроков для iOS. На данный момент я разделил это приложение на 3 модуля.
- UI
- Игровая логика
- сетей
У меня есть два публичных протокола в сетевом модуле:
public protocol ConnectionManager {
init(name: String)
var peers: Property<[String]> { get }
func invitePeer(at index: Int)
func respond(accept: Bool)
var invitation: Signal<String, NoError> { get }
var response: Signal<Bool, NoError> { get }
}
public protocol MessageTransmission {
func send(_ data: Data)
var data: Signal<Data, NoError> { get }
}
и на самом деле существует только одна конкретная реализация, которая соответствует обоим протоколам. Скажем так:
class Foo: ConnectionManager, MessageTransmission {
// ... codes omitted
}
Модуль пользовательского интерфейса получает имя от пользователя и использует его для инициализации Foo
, Затем он отображает соседних игроков в соответствии с peers
имущество. Когда пользователь отправляет приглашение начать новую игру, модуль пользовательского интерфейса направляет этот запрос ConnectionManager
и ConnectionManager
обрабатывает эти грязные работы.
Для модуля игровой логики он заботится только о передаче сообщений, но передача зависит от предыдущего шага "пригласить-ответить", потому что нам нужна цель для обмена сообщениями. (Концепции, относящиеся к цели, инкапсулированы, поэтому игровая логика знает только о том, что она может отправлять и получать сообщения).
Моя мысль такова: как только сеанс будет создан, т. Е. Как только приглашение получит ответ true
, модуль пользовательского интерфейса инициализирует игровую логику (может быть, экземпляр типа Game
) и передает ConnectionManager
(хотя модуль пользовательского интерфейса инициализирует экземпляр типа Foo
он хранит этот экземпляр как тип ConnectionManager
) к нему. Но проблема в том, ConnectionManager
не имеет ничего общего с MessageTransmission
если смотреть снаружи, даже если они реализованы одним типом внутри.
- Я могу сделать силовое дело по обе стороны, но это выглядит сложно.
- Я могу объединить эти два протокола, но тогда модуль пользовательского интерфейса и модуль игровой логики взаимодействуют с избыточным интерфейсом (интерфейс, который имеет больше возможностей, чем необходимо).
Какой путь мне выбрать? или есть ли лучший способ сделать это?
пс: Foo
может быть инициализирован только модулем пользовательского интерфейса, потому что это единственный, кто знает о name
, Также необходимо передать тот же экземпляр модулю игровой логики, поскольку существуют некоторые внутренние состояния (вещь, связанная с целью), которая была изменена в предыдущем взаимодействии с модулем пользовательского интерфейса и влияет на последующее взаимодействие с модулем игровой логики. Я не могу придумать другой способ сделать это.
1 ответ
Как я понял из твоего объяснения нужно пройти MessageTransmition
реализация к игровой логике но не хочу проходить ConnectionManager
,
Как только экземпляр MessageTransmission
зависит от результата приглашения (или принятия приглашения), сделанного ConnectionManager
вам нужно построить объект, соответствующий MessageTransmition
изнутри ConnectionManager
и перейти к игровой логике. Да, это означает, что вы соединяете эти две сущности, но в этом случае у них будут чистые обязанности.
Рассмотрим такой протокол:
protocol ConnectionManager {
func respond(accept: Bool) -> Signal<MessageTransmition, Error>
}
respond()
метод принимает приглашение и строит MessageTransmission
соответствует объекту в случае успеха или возвращает ошибку, если принять не удалось или приглашение отклонено. Этот объект вы можете передать игровой логике.