如何创建自己的自定义转换类

时间:2017-02-28 16:49:17

标签: scala

我有一个非常通用的消息对象,我从队列中回来:

case class Message(key: String, properties: Map[String, String])

然后我有一堆非常具体的类来表示消息,我使用properties.get(“type”)来确定它是哪个特定的消息:

sealed trait BaseMessage
case class LoginMessage(userId: Int, ....) extends BaseMessage
case class RegisterMessage(email: String, firstName: String, ....) extends BaseMessage

现在在我的代码中,我必须在许多地方从一般消息转换为特定消息,我想在一个地方创建它,如:

目前我正在做类似的事情:

val m = Message(....)
val myMessage = m.properties.get("type") match {
  case Some("login") => LoginMessage(m.properties("userID"), ...)
  case ...
}

我有什么选择可以减少scala中的麻烦?

2 个答案:

答案 0 :(得分:1)

我不知道您的所有上下文,但如果您不想在项目中添加其他库,我可以建议使用隐式转换。无论如何,隐式转换可以帮助您分离很多实现,或者根据需要“即时”覆盖它。

我们可以从定义实际上是函数的MessageConverter特征开始:

/**
  * Try[T] here is useful to track deserialization errors. If you don't need it you can use Option[T] instead.
  */
trait MessageConverter[T <: BaseMessage] extends (Message => Try[T])

现在定义一个包含两个实现的对象,并在#as[T]个实例上启用一个不错的Message方法:

object MessageConverters {
  /**
    * Useful to perform conversions such as:
    * {{{
    *   import MessageConverters._
    *
    *   message.as[LoginMessage]
    *   message.as[RegisterMessage]
    * }}}
    */
  implicit class MessageConv(val message: Message) extends AnyVal {
    def as[T <: BaseMessage : MessageConverter]: Try[T] =
      implicitly[MessageConverter[T]].apply(message)
  }

  // Define below message converters for each particular type

  implicit val loginMessageConverter = new MessageConverter[LoginMessage] {
    override def apply(message: Message): Try[LoginMessage] = {
      // Parse the properties and build the instance here or fail if you can't.
    }
  }
}

就是这样!它可能不是最佳解决方案,因为暗示会带来复杂性,并且使代码更难以遵循。但是,如果您遵循一个定义良好的结构来存储这些隐式值,并且要小心如何传递它们,那么您不应该有任何问题。

答案 1 :(得分:0)

您可以将properties地图转换为Json,并将其作为案例类阅读。假设地图的键与您的案例类字段具有相同的名称,您可以使用playjson编写格式化程序:

object LoginMessage {
    implicit val fmtLoginMessage = Json.format[LoginMessage]
}

如果字段名称不同,则必须手动指定读取对象。将代码转换为案例类的代码如下:

object BaseMessageFactory {
  def getMessage(msg: Message): Option[BaseMessage] = {
    val propertiesJson = Json.toJson(msg.properties)
    msg.properties.get("type").map {
      case "login" => propertiesJson.as[LoginMessage]
          ...
      case _ => //Some error
    }
  }
}

签名可能会有所不同,具体取决于您希望如何处理错误处理。