如何提供辅助方法来构建地图 [英] How to provide helper methods to build a Map

查看:41
本文介绍了如何提供辅助方法来构建地图的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有很多使用相同的键(查询MongoDB)构建 Map 的客户端代码。

I have a lot of client code that build Map using the same keys (to query MongoDB).

我的想法是提供隐藏键的辅助方法。

My idea is to provide helper methods that hide keys.

首先尝试使用默认参数(参见 object Builder 以下),但客户端需要处理 Option

First try, I have used default parameters (cf object Builder below) but the client hava to deal with Option

我现在使用了构建器模式(cf class Builder 下面)

I now use a builder pattern (cf class Builder below)

还有更好的方法吗?

class Builder {
  val m = collection.mutable.Map[String, Int]()
  def withA(a: Int) = {m += (("a", a))}
  def withB(b: Int) = {m += (("b", b))}
  def withC(c: Int) = {m += (("c", c))}
  def build = m.toMap
}

object Builder {
  def build1(a: Option[Int] = None, b: Option[Int] = None, c: Option[Int] = None): Map[String, Int] = {
    val optPairs = List(a.map("a" -> _),
      b.map("b" -> _),
      c.map("c" -> _))
    val pairs = optPairs.flatten
    Map(pairs: _*)
  }
}

object Client {
  def main(args: Array[String]) {
    println(Builder.build1(b = Some(2)))

    println(new Builder().withB(2))
  }
}


推荐答案

一种简单的解决方案,避免在调用 Builder.build1 定义一个隐式转换,以自动将任何值包装为 Some

An easy solution to avoid having to deal with options when calling Builder.build1 is to define an implicit conversion to automatically wrap any value into an Some:

implicit def wrap[T]( x: T ) = Some( x )

繁荣,您可以省略包装并直接这样做:

And boom, you can omit the wrapping and directly do:

scala> Builder.build1( a = 123, c = 456 )
res1: Map[String,Int] = Map(a -> 123, c -> 456)

但是,鉴于期权无处不在,并且您不希望将这种一般的转换纳入范围,这非常危险。
要解决此问题,您可以定义自己的 option类,仅用于定义这些可选参数:

However, this is pretty dangerous given that options are pervasive and you don't want to pull such a general converion into scope. To fix this you can define your own "option" class that you'll use just for the purpose of defining those optional parameters:

abstract sealed class OptionalArg[+T] {
  def toOption: Option[T]
}
object OptionalArg{
  implicit def autoWrap[T]( value: T  ): OptionalArg[T] = SomeArg(value)
  implicit def toOption[T]( arg: OptionalArg[T] ): Option[T] = arg.toOption
}
case class SomeArg[+T]( value: T ) extends OptionalArg[T] {
  def toOption = Some( value )
}
case object NoArg extends OptionalArg[Nothing] {
  val toOption = None
}

然后可以重新定义 Build.build1 为:

def build1(a: OptionalArg[Int] = NoArg, b: OptionalArg[Int] = NoArg, c: OptionalArg[Int] = NoArg): Map[String, Int]

然后再次,您可以直接调用 Build.build1 而不显式地包装参数w ith Some

And then once again, you can directly call Build.build1 without explicitely wrapping the argument with Some:

scala> Builder.build1( a = 123, c = 456 )
res1: Map[String,Int] = Map(a -> 123, c -> 456)

与现在的显着区别是,我们不再将危险的广泛转换为应付。

With the notable difference that now we are not pulling anymore a dangerously broad conversion into cope.

更新:为响应下的注释,为了进一步满足我的需要,arg可以是单个值或列表,而我今天我的客户代码中有可怕的Some(List(sth))。

您可以添加另一种转换,以将单个参数包装到一个元素列表中:

You can add another conversion to wrap individual parameters into one element list:

implicit def autoWrapAsList[T]( value: T  ): OptionalArg[List[T]] = SomeArg(List(value))

然后说您的方法需要这样的可选列表:

Then say that your method expects an optional list like this:

def build1(a: OptionalArg[List[Int]] = NoArg, b: OptionalArg[Int] = NoArg, c: OptionalArg[Int] = NoArg): Map[String, Int] = {
  val optPairs = List(a.map("a" -> _.sum),
    b.map("b" -> _),
    c.map("c" -> _))
  val pairs = optPairs.flatten
  Map(pairs: _*)
}

您现在可以传递单个元素或列表(或像以前一样,根本没有参数):

You can now either pass an individual element or a list (or just like before, no argument at all):

scala> Builder.build1( a = 123, c = 456 )
res6: Map[String,Int] = Map(a -> 123, c -> 456)

scala> Builder.build1( a = List(1,2,3), c = 456 )
res7: Map[String,Int] = Map(a -> 6, c -> 456)

scala> Builder.build1( c = 456 )
res8: Map[String,Int] = Map(c -> 456)

最后一个警告:即使我们定义了自己的 option类,您还是应该谨慎使用隐式转换,这确实是正确的,
还是要花一些时间来平衡在您的用例中,便利性是否值得冒险。

One last warning: even though we have defined our very own "option" class, it is still true that you should always use implicit conversions with some care, so take some time to balance whether the convenience is worth the risk in your use case.

这篇关于如何提供辅助方法来构建地图的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆