бэкендом по типу RPC, и встал момент того, как отправлять запросы (и, соответственно, получать данные и десериализовать их). Сделал вот функцию с такой сигнатурой
suspend fun <T: ResponsePayload> sendCall(
action: Events,
data: SocketRequest,
responseType: Type
): SuspendableResult<T, Exception> {
и с маркер интерфейсом для констрейнта дженерика для ответа, но т.к. маркер интерфейсы часто считается как анти-паттерн, может у кого-нибудь есть идеи, как реализовывать подобное? (У ответов нет ничего общего, от чего они могли бы наследоваться)
interface ResponsePayload
data class FetchUserProfileResponse(val profile: UserProfile): ResponsePayload
data class UpdateNotificationSettingsResponse(val userId: String): ResponsePayload
На сколько я знаю вместо маркера используется анотация
Я, к сожалению, либо не понял, как работают аннотации, либо для моего юз кейса они не очень подходили, а именно использовать одну функцию для возврата разных типов данных, которые известны вызываемому и должны быть ограничены определенным кругом дата классов
У Вас же вроде другая задача: ограничить входные аргументы
Конкретно в этом случае, да, я просто описал более общее, что мне нужно, на тот случай, если в целом мой подход не совсем оптимален
А почему вам Any? в качестве аппер баунда не нравится?
Хотелось бы хотя бы на этапе компиляции иметь проверку/ограничение, что этот метод будет вызван только с теми типами, которые относятся к респонзам) например, чтобы точно быть уверенными, что все нормально распарсится, да и кажется такой подход логичным, выставить ограничение (например, так понятнее для другого разработчика может быть)
ну, общее поведение у ваших респонсов здесь - это то, что их можно распарсить из жсона, т.е. контракт выглядит примерно fun parse(json: String): T, что и можно сделать объективным компайл-тайм ограничением. Потому что с маркер интерфейсами мне, как абстрактному дебилу, это не мешает написать что-нибудь вроде class MusorClass : ResponsePayload Немного сильнее с моей точки зрения было бы сделать что-то вроде interface PayloadParser<T> { fun parse(json: String): T } и изменить sendCall так, чтобы он ещё принимал инстанс этого пейлоад парсера и убрать маркер интерфейс совсем. Таким образом у вас получается, что этот метод действительно можно вызвать только с теми типами, которые можно распарсить Конечно, это приносит немного проблем в виде бойлерплейта на реализации, плюс нужно откуда-то брать инстансы Gson-а, но что делать, в котлине нет тайпклассов, плюс замусоривается апи этого метода В противном случае да, я не вижу лучше решения ограничить дтошки, кроме как маркер интерфейсом
т.е. поместить метод десериализации в каждый возвращаемый тип?
Примерно пример: data class FetchUserProfileResponse(val profile: UserProfile) class FetchUserProfileResponseParser(private val gson: Gson) : PayloadParser<FetchUserProfileResponse> { override fun parse(json: String): FetchUserProfileResponse = ... } val parser: FetchUserProfileResponseParser = ... val response: SuspendableResult<FetchUserProfileResponse, Exception> = sendCall(parser, action, data)
Обсуждают сегодня