I am trying to solve a problem that handles conversion from one Temperature Unit to the other(Celsius, Kelvin, Fahrenheit).
In Java I need to create an interface and provide multiple implementations that encapsulate the Input Type and return the result as a unit of the output type. e.g Kelvin to Celsius or celsius to fahrenheit etc. I have refactored my code in scala to following but still I feel it breaks the Open closed principle, since in case I need to add another type I need to change the existing code.Any suggestions to keep the code functional as well as adherent to the Open closed principle Please ignore the logic for conversion
object TempConverter extends App {
object UnitType extends Enumeration {
type EnumType = Value
val cel, fah, kel = Value
}
def convert(x: Double, i:UnitType.Value,o:UnitType.Value) = {
strategy(i,o)(x)
}
def strategy(inputType: UnitType.Value, outputType: UnitType.Value) = {
inputType match {
case UnitType.cel => celsius(outputType)
case UnitType.kel => kelvin(outputType)
case UnitType.fah => fahrenheit(outputType)
}
}
def celsius(outputType: UnitType.Value) = {
outputType match {
case UnitType.fah => x: Double => x * 1.8 + 32
case UnitType.kel => x: Double => x * 1.8 + 32
}
}
def kelvin(outputType: UnitType.Value) = {
outputType match {
case UnitType.cel => x: Double => x - 273.5
case UnitType.fah => x: Double => x * 1.8 + 32
}
}
def fahrenheit(outputType: UnitType.Value) = {
outputType match {
case UnitType.cel => x: Double => x * 1.8 + 32
case UnitType.fah => x: Double => x * 1.8 + 32
}
}
println(convert(32.0, UnitType.cel, UnitType.fah))
}
I would do the following:
toKelvin
and from Kelvin
method.a
to unit b
is just: b.fromKelvin(a.toKelvin())
Turns out adding methods to Enumerations is trickier in Scala than Java, so here's an implementation with singletons implementing a trait:
trait TemperatureUnit {
def toKelvin(value : Double): Double
def fromKelvin(value : Double): Double
def convert(value : Double, unit : TemperatureUnit) : Double = fromKelvin(unit.toKelvin(value))
}
object Kelvin extends TemperatureUnit {
def toKelvin(value : Double) = value
def fromKelvin(value : Double) = value
}
object Celsius extends TemperatureUnit {
def toKelvin(value : Double) = value + 273.5
def fromKelvin(value : Double) = value - 273.5
}
Then converting Kelvin to Celsius is just:
scala> Celsius.convert(100,Kelvin)
res0: Double = -173.5
You should probably also add a wrapper class so that you're not passing around bare Double
s (which can accidentally be used as lengths, timestamps, etc. without a compiler warning).
class Temperature (value: Double, unit: TemperatureUnit) {
def to(new_unit: TemperatureUnit) = new Temperature(new_unit.convert(value,unit),new_unit)
}
Then when you write
new Temperature(10,Celsius).to(Kelvin)
There's no ambiguity left.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With