【问题标题】:Scala Best Practices: Trait Inheritance vs EnumerationScala 最佳实践:特征继承与枚举
【发布时间】:2011-10-08 23:51:03
【问题描述】:

我目前正在试验 Scala 并寻找最佳实践。我发现自己有两种相反的方法来解决一个问题。我想知道哪个更好,为什么,哪个更传统,如果你知道其他更好的方法。我觉得第二个更漂亮。

1.基于枚举的解决方案

import org.squeryl.internals.DatabaseAdapter
import org.squeryl.adapters.{H2Adapter, MySQLAdapter, PostgreSqlAdapter}
import java.sql.Driver

object DBType extends Enumeration {
  val MySql, PostgreSql, H2 = Value

  def fromUrl(url: String) = {
    url match {
      case u if u.startsWith("jdbc:mysql:") => Some(MySql)
      case u if u.startsWith("jdbc:postgresql:") => Some(PostgreSql)
      case u if u.startsWith("jdbc:h2:") => Some(H2)
      case _ => None
    }
  }
}

case class DBType(typ: DBType) {
  lazy val driver: Driver = {
    val name = typ match {
      case DBType.MySql => "com.mysql.jdbc.Driver"
      case DBType.PostgreSql => "org.postgresql.Driver"
      case DBType.H2 => "org.h2.Driver"
    }
    Class.forName(name).newInstance().asInstanceOf[Driver]
  }
  lazy val adapter: DatabaseAdapter = {
    typ match {
      case DBType.MySql => new MySQLAdapter
      case DBType.PostgreSql => new PostgreSqlAdapter
      case DBType.H2 => new H2Adapter
    }
  }
}

2。基于单例的解决方案

import org.squeryl.internals.DatabaseAdapter
import org.squeryl.adapters.{H2Adapter, MySQLAdapter, PostgreSqlAdapter}
import java.sql.Driver

trait DBType {
  def driver: Driver
  def adapter: DatabaseAdapter
}

object DBType {
  object MySql extends DBType {
    lazy val driver = Class.forName("com.mysql.jdbc.Driver").newInstance().asInstanceOf[Driver]
    lazy val adapter = new MySQLAdapter
  }

  object PostgreSql extends DBType {
    lazy val driver = Class.forName("org.postgresql.Driver").newInstance().asInstanceOf[Driver]
    lazy val adapter = new PostgreSqlAdapter
  }

  object H2 extends DBType {
    lazy val driver = Class.forName("org.h2.Driver").newInstance().asInstanceOf[Driver]
    lazy val adapter = new H2Adapter
  }

  def fromUrl(url: String) = {
    url match {
      case u if u.startsWith("jdbc:mysql:") => Some(MySql)
      case u if u.startsWith("jdbc:postgresql:") => Some(PostgreSql)
      case u if u.startsWith("jdbc:h2:") => Some(H2)
      case _ => None
    }
  }
}

【问题讨论】:

    标签: scala object singleton enumeration


    【解决方案1】:

    如果你声明一个sealed trait DBType,你可以通过穷举检查对其进行模式匹配(即,如果你忘记了一个案例,Scala 会告诉你)。

    无论如何,我不喜欢 Scala 的 Enumeration,而且我并不孤单。我从不使用它,如果枚举确实是最干净的解决方案,最好使用 Java 的枚举用 Java 编写它。

    【讨论】:

    • 我完全同意。 Scala 枚举绝对没用。它们只提供顺序值的自动生成,我怀疑有人根本不需要。相反,没有通过字符串 id 查找值的好方法(下面使用反射),也没有合法的方法可以从 Enumeration#Value 解析 Enumeration。
    【解决方案2】:

    对于这种特殊情况,您实际上并不需要每种数据库类型的类;这只是数据。除非实际情况要复杂得多,否则我会使用基于映射和字符串解析的解决方案来最大程度地减少代码重复量:

    case class DBRecord(url: String, driver: String, adapter: () => DatabaseAdapter) {}
    
    class DBType(record: DBRecord) {
      lazy val driver = Class.forName(record.driver).newInstance().asInstanceOf[Driver]
      lazy val adapter = record.adapter()
    }
    
    object DBType {
      val knownDB = List(
        DBRecord("mysql", "com.mysql.jdbc.Driver", () => new MySQLAdapter),
        DBRecord("postgresql", "org.postgresql.Driver", () => new PostgreSqlAdapter),
        DBRecord("h2", "org.h2.Driver", () => new H2Adapter)
      )
    
      val urlLookup = knownDB.map(rec => rec.url -> rec).toMap
    
      def fromURL(url: String) = {
        val parts = url.split(':')
        if (parts.length < 3 || parts(0) != "jdbc") None
        else urlLookup.get(parts(1)).map(rec => new DBType(rec))
      }
    }
    

    【讨论】:

      【解决方案3】:

      我会选择单例变体,因为它允许更清晰的子类化。

      此外,您可能需要执行特定于 db 的操作/覆盖,因为某些查询/子查询/操作符可能不同。

      但我会尝试这样的:

      import org.squeryl.internals.DatabaseAdapter
      import org.squeryl.adapters.{H2Adapter, MySQLAdapter, PostgreSqlAdapter}
      import java.sql.Driver
      
      abstract class DBType(jdbcDriver: String) {
        lazy val driver = Class.forName(jdbcDriver).newInstance().asInstanceOf[Driver]
        def adapter: DatabaseAdapter
      }
      
      
      object DBType {
        object MySql extends DBType("com.mysql.jdbc.Driver") {
          lazy val adapter = new MySQLAdapter
        }
      
        object PostgreSql extends DBType("org.postgresql.Driver") {
          lazy val adapter = new PostgreSqlAdapter
        }
      
        object H2 extends DBType("org.h2.Driver") {
          lazy val adapter = new H2Adapter
        }
      
        def fromUrl(url: String) = {
          url match {
            case _ if url.startsWith("jdbc:mysql:") => Some(MySql(url))
            case _ if url.startsWith("jdbc:postgresql:") => Some(PostgreSql(url))
            case _ if url.startsWith("jdbc:h2:") => Some(H2(url))
            case _ => None
          }
      
      }
      

      如果这有帮助,请考虑对此 +1 :)

      【讨论】:

      • 特征不能有参数。那应该是一个抽象类吗?
      • 是的,抱歉,我刚刚复制了你的代码,忘记了那里的特征。
      • 对不起!早上 6 点,还没睡 ;) 只是想帮忙。
      • 我不认为这项服务是乞求分数。有尊严的人。我必须在这里标记什么已回答?问题是哪个更好,为什么,哪个更传统,如果你知道其他更好的方法 - 以及复制粘贴我的带有原始修正的代码与此有什么关系?
      • 你想要一个有理由的意见,我给了你一个小的代码改进。对不起,不会再发生了。此外,如果人们不会经常忘记将他们的问题标记为已回答,那么有人可能能够正确使用未回答的标签。
      猜你喜欢
      • 1970-01-01
      • 2021-04-20
      • 1970-01-01
      • 1970-01-01
      • 2014-11-07
      • 2021-06-24
      • 1970-01-01
      • 2020-11-11
      • 2013-11-09
      相关资源
      最近更新 更多