2014-10-03 102 views
3

我有configurer支持連鎖式的,像這樣:鏈風格+斯卡拉選項

val configurer = Configurer("init").propA("a").propB(3).propC("bla-bla") 

它的第三方的lib,我不能改變。

我有我的

案例類配置(propA:選項[字符串],PROPB:選項[INT],propC: 選項[字符串])

現在我需要用給定的config對象構建我的configurer,則應調用方法propX,如果在config中設置了相應的值。

以功能性的方式做到這一點的最佳方式是什麼?

我不喜歡這個

val configurer = Configurer("init") 
val withPropA = config.propA.map(configurer.propA).getOrElse(configure) 
val withPropB = config.propB.map(configurer.propB).getOrElse(withPropA) 
val withPropC = config.propC.map(configurer.propC).getOrElse(withPropB) 

只是覺得應該有一種優雅的方式。

+0

這是一個蹩腳的API,只需將這個東西包裝在COnfigurer伴侶對象的'fromConfig'函數中並用它來完成:) – vptheron 2014-10-03 16:58:09

回答

1

你可以用var來完成它,通常這是scala中代碼不好的一個標誌,但在這種情況下,我認爲它是絕對可以接受的。

def buildConfigurer(propA: Option[String], propB: Option[Int], propC: Option[String]) = { 
    var configurer = new Configurer("init") 
    propA.foreach(a => configurer = configurer.propA(a)) 
    propB.foreach(b => configurer = configurer.propB(b)) 
    propC.foreach(c => configurer = configurer.propC(c)) 
    configurer 
} 
+0

如果Configurer是Java類,那麼它很可能是可變的。如果'configurer'的情況可以是val並且可以用作:'conf.propA.foreach(a => configurer.propA(a))' – roterl 2014-10-04 01:08:27

0

既然你特別問到一個功能性的方式這樣做,我建議使用上轉換成Some所需要的功能和Noneidentity每個選項摺疊:

config.propA.fold(identity[Configurer] _)(a => _ propA a) andThen 
config.propB.fold(identity[Configurer] _)(b => _ propB b) andThen 
config.propC.fold(identity[Configurer] _)(c => _ propC c) 

如果您真的很有冒險精神,你可以用斯卡拉斯讓它更加優雅:

import scalaz._, Scalaz._ 

config.propA.map(a => Endo[Configurer](_ propA a)).orZero |+| 
config.propB.map(b => Endo[Configurer](_ propB b)).orZero |+| 
config.propC.map(c => Endo[Configurer](_ propC c)).orZero 

在真正的代碼中,你可能想要使用不過,尤金的解決方案,因爲你只是包裝一個不理想的API,重要的是從這裏開始清楚。

0

我會使用類似@ EugeneZhulenev的解決方案,但與Option.fold代替foreach到仍然留不變(不打算通過@TravisBrown提出的高階/ scalaz版本):

def buildConfigurer(cfg: Config): Configurer = { 
    val with0 = new Configurer("init") 
    val withA = cfg.propA.fold(with0)(with0.propA(_)) 
    val withB = cfg.propB.fold(withA)(withA.propB(_)) 
    val withC = cfg.propC.fold(withB)(withB.propC(_)) 
    withC 
}