Как выставить разные функциональные возможности одного типа для разных модулей?

Я работаю над небольшим приложением для двух игроков для 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 соответствует объекту в случае успеха или возвращает ошибку, если принять не удалось или приглашение отклонено. Этот объект вы можете передать игровой логике.

Другие вопросы по тегам