Reputation: 2386
In Scala you often use Java APIs which use the Builder pattern, e.g Google Maps Java Client (used in the example below).
I use a Scala case class in our app to gather all values that I will use then for the Builder pattern of the Java API. I design optional values of the Builder API, idiomatically with Scalas Option type. However I scratch my head how to do this nicer than this example:
case class WlanHint(
mac: String,
signalStrength: Option[Int],
age: Option[Int],
channel: Option[Int],
signalToNoiseRatio: Option[Int]
)
val builder = WifiAccessPoint.WifiAccessPointBuilder().MacAddress(wlanHint.mac)
val b = wlanHint.signalStrength.map(signalStrength => builder.SignalStrength(signalStrength)).getOrElse(builder)
val b2 = wlanHint.age.map(age => b.Age(age)).getOrElse(b)
val b3 = wlanHint.channel.map(channel => b2.Channel(channel)).getOrElse(b2)
val b4 = wlanHint.signalToNoiseRatio.map(signalToNoiseRatio => b3.SignalToNoiseRatio(signalToNoiseRatio)).getOrElse(b3)
// TODO is there a better way to do this above?
b4.createWifiAccessPoint())
Upvotes: 0
Views: 355
Reputation: 20611
You can hide the builder calls within the case class
as methods and also (since a Java builder is typically mutable) use the foreach
method on Option
:
case class WlanHint(
mac: String,
signalStrength: Option[Int],
age: Option[Int],
channel: Option[Int],
signalToNoiseRatio: Option[Int]
) {
def asWifiAccessPoint: WifiAccessPoint = { // guessing about return type...
val builder = WifiAccessPoint.WifiAccessPointBuilder().MacAddress(mac)
// The ()s may not be needed if not warning about a discarded value...
signalStrength.foreach { ss => builder.SignalStrength(ss); () }
age.foreach { a => builder.Age(a); () }
channel.foreach { c => builder.Channel(c); () }
signalToNoiseRatio.foreach { snr => builder.SignalToNoiseRatio(snr); () }
builder.createWifiAccessPoint()
}
}
The first foreach
in this example ends up being effectively the same as:
if (signalStrength.isDefined) {
builder.SignalStrength(signalStrength.get)
}
If WlanHint
is only intended to reify the arguments to the builder, it might make sense to have it be a Function0[WifiAccessPoint]
:
case class WlanHint(
mac: String,
signalStrength: Option[Int],
age: Option[Int],
channel: Option[Int],
signalToNoiseRatio: Option[Int]
) extends Function0[WifiAccessPoint] {
def apply(): WifiAccessPoint = {
// same body as asWifiAccessPoint in previous snippet
}
}
This would then allow you to write:
val accessPoint = WifiAccessPoint("00:01:02:03:04:05", None, None, None, None)()
Upvotes: 2