2014-10-03 3 views
3

Я получил configurer, поддерживающий стиль цепи, как так:цепи

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

это третья сторона Lib, что я не могу изменить.

И у меня

случай класс Config (Propa: Опция [String], propB: Option [Int], propC: Option [String])

Теперь мне нужно постройте мой configurer с данным объектом config, следует вызвать метод 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, просто оберните это в функцию 'fromConfig' в сопутствующем объекте' COnfigurer' и сделайте с ним :) – vptheron

ответ

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, то он, скорее всего, изменен. Если это «конфигуратор» дела может быть val и использовать как: 'conf.propA.foreach (a => configurer.propA (a))' – roterl

0

Поскольку вы специально просили о этом в функциональном смысле, я предложил бы использовать складку по каждому из вариантов, который преобразует Some в нужную функцию и None в identity:

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) 

Если у вас «повторно действительно авантюризма, вы можете сделать это немного более элегантное Scalaz:

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 остаться неизменны, тем не менее (без перехода на версию более высокого порядка/scalaz предложенной @TravisBrown):

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 
} 
Смежные вопросы