Scala最佳实践:特征继承与枚举

时间:2011-10-08 23:51:04

标签: scala object singleton enumeration

我目前正在尝试使用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
    }
  }
}

3 个答案:

答案 0 :(得分:12)

如果您声明sealed trait DBType,则可以使用详尽的检查对其进行模式匹配(即,Scala会告诉您是否忘记了一个案例)。

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

答案 1 :(得分:10)

对于这个特定的情况,你并不真正需要每个数据库类型的类;这只是数据。除非真实情况要复杂得多,否则我会使用基于地图和字符串解析的解决方案来最小化代码重复的数量:

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

答案 2 :(得分:4)

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

此外,您可能需要执行特定于数据库的事物/覆盖,因为某些查询/子查询/运算符可能不同。

但我会尝试这样的事情:

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这个:)