2014-02-06 87 views
0

我有很多使用相同鍵(查詢MongoDB)構建Map的客戶端代碼。如何提供幫助程序方法來構建地圖

我的想法是提供隱藏鍵的幫助器方法。

先試試,我已經使用默認參數(以下CF object Builder),但客戶端HAVA處理Option

我現在使用生成器模式(以下CF class Builder

有沒有更好的辦法?

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

回答

2

一個簡單的解決方案,以避免調用Builder.build1當定義一個隱式轉換到自動包裝任何值轉換成Some處理選項:

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

和吊杆,可以省略包裝並直接做:

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

但是,這是非常危險的,因爲選項是普遍的,你不想拉這樣一個普通的進入範圍。 爲了解決這個問題,你可以定義自己的「選項」類,你只使用定義這些可選參數的目的:

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而不Some明確地包裹的說法:

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

由於顯着的差異,現在我們不再需要進行危險廣泛的轉換來應對了。


UPDATE:在回答下面「的評論在我需要走得更遠,ARG可以是單個數值或者列表,我有可怕的一些(名單(某事))在我的客戶今天代碼」

您可以添加另一個轉換到各個參數包裝成一個元素列表:

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

然後說你的方法需要這樣的可選列表:

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: _*) 
} 

您可以現在要麼通過一個單獨的元素或列表(或像以前一樣,沒有參數的話):

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) 

最後一個警告:儘管我們已經定義了自己的「選項「類,但仍應該始終謹慎使用隱式轉換,因此需要一些時間來平衡便利是否值得您的用例中的風險。

+0

不錯的把戲!儘管如此,我發現構建器的方法更強大(在不同的客戶端方法中重用部分映射) –

+0

的確如此,但它也是更多的樣板文件,所以如果有定義的話,通常只會使用這個路徑。我上面的解決方案可能並不總是合適的,但應用起來要少得多。 –

+0

在我的需求中更進一步,arg可以是單個值或列表,並且我今天在我的客戶端代碼中有一些(List(sth))可怕的。 –