scala - 实现策略模式的函数式方法

标签 scala design-patterns

我正在尝试解决一个处理从一种温度单位到另一种温度单位(摄氏度、开尔文、华氏度)转换的问题。

在Java中,我需要创建一个接口(interface)并提供多个实现来封装输入类型并将结果作为输出类型的单元返回。例如开尔文到摄氏度或摄氏度到华氏度等。我已经在 scala 中将我的代码重构为以下内容,但我仍然觉得它打破了开放封闭原则,因为如果我需要添加另一种类型,我需要更改现有代码。任何建议保持代码功能并遵守开放封闭原则 请忽略转换逻辑

    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))

}

最佳答案

我会执行以下操作:

  • 单位枚举。
  • 每个单元都有一个 toKelvinfrom Kelvin 方法。
  • 然后从单位a转换为单位b只需:b.fromKelvin(a.toKelvin())
  • 添加新单元只需要在新单元上实现这两个方法。

事实证明,在 Scala 中向枚举添加方法比在 Java 中更棘手,因此这里有一个使用单例实现特征的实现:

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
}

然后将开尔文转换为摄氏度就是:

scala> Celsius.convert(100,Kelvin)
res0: Double = -173.5

您可能还应该添加一个包装类,这样您就不会传递裸露的 Double(它可能会意外地用作长度、时间戳等,而不会出现编译器警告)。

class Temperature (value: Double, unit: TemperatureUnit) {
  def to(new_unit: TemperatureUnit) = new Temperature(new_unit.convert(value,unit),new_unit)
}

然后当你写的时候

new Temperature(10,Celsius).to(Kelvin)

没有任何歧义。

关于scala - 实现策略模式的函数式方法,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/29195374/

相关文章:

design-patterns - 如何在这里避免循环依赖

design-patterns - 访问者模式和递归

java.lang.Boolean 到 scala.Boolean 的问题

scala - NonEmptyList 的 Monad 转换器?

scala - 如何在 Scala 中将元素附加或前置到元组

f# - 与 fsharp 中的 typeof 匹配

c++ - 在硬件接口(interface)之间切换的最佳设计模式

java - 有没有办法获得SQS队列的平均服务时间?

scala - Slick 3.0 通用 CRUD 实现中的类型参数绑定(bind)错误

java - 什么应该是最好的设计方法