2017-02-28 50 views
0

我有一个非常普通的消息对象,我得到一个队列就像回到:如何创建自己的自定义转换

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

然后我有一堆代表的消息非常具体的类,我使用properties.get(“类型”),以确定它是特定消息:

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 ... 
} 

我必须在作出这一阶不太麻烦什么选择?

+0

不使用'Map [String,String]'来存储初学者的任意属性。恢复这些信息并不容易。 –

+0

无形可能可以帮助,如果你想保持类型安全 – LMeyer

+0

@MichaelZajac该类是从消息队列系统,不能改变。 – Blankman

回答

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 
    } 
    } 
} 

签名可能取决于您希望如何处理错误处理不同。

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. 
    } 
    } 
} 

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

相关问题