2017-02-03 9 views
0

Для того, чтобы иметь возможность обрабатывать большие объемы различных типов запросов я создал .proto файл вроде этого:Обработка грузов различных сообщений-типов во время выполнения в элегантный способ

message Message 
{ 
    string typeId = 1; 
    bytes message = 2; 
} 

Я добавил typeId так, что один знает, что фактический protobufbytes представляет. (Самоописывающийся)

Теперь моя проблема заключается в том, чтобы использовать различные «конкретные типы» элегантным способом. (Примечание: Все отлично работает, если я просто использовать в switch-case -like подход)

Я думал о решении, как это:

1) имеют признака различные обработчики должны реализовать, например:

trait Handler[T] 
{ 
    def handle(req: T): Any 
} 

object TestHandler extends Handler[Test] 
{ 
    override def handle(req: Test): String = 
    { 
    s"A success, $req has been handled by TestHandler 
    } 
} 

object OtherHandler extends Handler[Other] 
{ 
    override def handle(req: Other): String = 
    { 
    s"A success, $req has been handled by OtherHandler 
    } 
} 

2) обеспечивают своего рода реестр для запроса правильный обработчик для данного сообщения:

val handlers = Map(
    Test -> TestHandler, 
    Other -> OtherHandler 
) 

3) Если запрос приходит он идентифицирует себя, поэтому нам нужен другой Mapper:

val reqMapper = Map(
    "Test" -> Test 
    "Other" -> Other 
) 

4) Если запрос приходит, справиться с этим:

val request ... 
// Determine the requestType 
val requestType = reqMapper(request.type) 
// Find the correct handler for the requestType 
val handler = handlers(requestType) 
// Parse the actual request 
val actualRequest = requestType.parse(...) // type of actualRequest can only be Test or Other in our little example 

Теперь, пока здесь все не выглядит хорошо и денди, но потом эта линия разбивает весь мой мир:

handler.handle(actualRequest) 

это приводит к:

Тип несоответствие; найдено: com.trueaccord.scalapb.GeneratedMessage с продуктом с com.trueaccord.scalapb.Message [_>: tld.test.proto.Message.Test с tld.test.proto.Message.Other <: com.trueaccord.scalapb. GeneratedMessage с продуктом] с com.trueaccord.lenses.Updatable [_>: tld.test.proto.Message.Other с tld.test.proto.Message.Test <: com.trueaccord.scalapb.GeneratedMessage с продуктом] {def companion : Сериализуемый} требуется: _1

насколько я понимаю - поправьте меня здесь, если я ошибаюсь - компилятор не может быть уверен, что здесь, что actualRequest является «handable» обработчиком. Это означает, что ему не хватает знаний о том, что actualRequest определенно находится где-то в этом mapper И ТАКЖЕ, что для него есть handler.

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

Итак, как я могу это преодолеть, элегантно?

ответ

0

Я остановился для этого решения сейчас (в основном thesamet годов, немного адаптированный для моего конкретного сценария использования)

trait Handler[T <: GeneratedMessage with Message[T], R] 
{ 
    implicit val cmp: GeneratedMessageCompanion[T] 
    def handle(bytes: ByteString): R = { 
     val msg: T = cmp.parseFrom(bytes.newInput()) 
     handler(msg) 
    } 

    def apply(t: T): R 
} 

object Test extends Handler[Test, String] 
{ 
    override def apply(t: Test): String = s"$t received and handled" 

    override implicit val cmp: GeneratedMessageCompanion[Test] = Test.messageCompanion 
} 
2

Ваши типы теряются при использовании обычной Карты.для например

object Test{} 
object Other{} 
val reqMapper = Map("Test" -> Test,"Other" -> Other) 
reqMapper("Test") 
res0: Object = [email protected] // the type is lost here and is set to java.lang.Object 

самого idomatic способ решения этой проблемы является использование шаблона соответствия

request match { 
    case x: Test => TestHandler(x) 
    case x: Other => OtherHandler(x) 
    case _ => throw new IllegalArgumentException("not supported") 
} 

, если вы все еще хотите использовать карты для хранения типа манипулятора отношения рассмотреть HMap обеспечивается бесформенными here

Гетерогенные карты

Shapeless обеспечивает гетерогенная карта, которая поддерживает произвольное связи между типом ключа и соответствующим типом значения,

+0

Да, я хочу, чтобы избежать сопоставления с образцом. Он поставляется с той же стоимостью обслуживания, что и «switch-case». Для новых типов я просто хочу зарегистрировать новые обработчики и карты. – Sorona

+0

@ Sorona, что с предложением HMap будет работать для вас? –

+0

Никогда не использовал 'HMap' раньше и предпочел бы чистое решение Scala без дополнительной зависимости. – Sorona

1

Один трюком вы можете использовать это, чтобы захватить объект компаньона, как неявные, и объединить синтаксический анализ и обработку в одной функции где тип доступен для компилятора:

case class Handler[T <: GeneratedMessage with Message[T]](handler: T => Unit)(implicit cmp: GeneratedMessageCompanion[T]) { 
    def handle(bytes: ByteString): Unit = { 
    val msg: T = cmp.parseFrom(bytes.newInputStream) 
    handler(t) 
    } 
} 

val handlers: Map[String, Handler[_]] = Map(
    "X" -> Handler((x: X) => Unit), 
    "Y" -> Handler((x: Y) => Unit) 
) 

// To handle the request: 
handlers(request.typeId).handle(request.message) 

Кроме того, обратите внимание на any.proto, который определяет структуру, очень похожую на ваш Message. Это не решит вашу проблему, но вы можете воспользоваться ее методами pack и unpack.

+0

Любой, еще не доступен в Scala.js, но спасибо, я посмотрю – Sorona

+0

Строка 4 должна быть 'handler (msg)' Я думаю;) – Sorona

+0

Исправить, обновить ответ. – thesamet