2011-10-08 75 views
11

我目前正在试验斯卡拉并寻找最佳实践。我发现自己有两种相反的方法来解决单个问题。我想知道哪个更好,为什么,哪些更传统,以及是否知道其他更好的方法。第二个看起来更漂亮。斯卡拉最佳实践:特质继承与枚举

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

回答

12

如果要声明sealed trait DBType,它可以模式匹配与全面性检查(即,如果您忘记了一个案例,Scala会告诉您)。

无论如何,我不喜欢斯卡拉的Enumeration,我并不孤单。我从来没有使用它,如果有枚举实际上是最干净的解决方案,最好用Java的枚举将它编写成Java。

+0

我完全同意。 Scala枚举绝对没用。他们只提供自动生成连续值,我怀疑有人需要。相反,没有好方法通过字符串id查找值(反射在下面使用),并且没有合法的方法来解决来自枚举#值的枚举。 –

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

+0

性状不能有参数。那应该是一个抽象类吗? –

+0

对不起,我刚刚复制你的代码,忘记了那里的特质。 –

+0

你的意思是你复制了mojojojo的代码。 –

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