2014-10-03 73 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 
}