2011-10-08 18 views
11

Attualmente sto sperimentando Scala e alla ricerca di migliori pratiche. Mi sono trovato ad avere due approcci opposti per risolvere un singolo problema. Mi piacerebbe sapere quale è meglio e perché, che è più convenzionale, e se forse conosci qualche altro approccio migliore. La seconda sembra più carina per me.Scala Best Practices: Trait Inheritance vs Enumeration

1. enumerazione basata soluzione

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. soluzione Singleton basata

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

risposta

12

Se si dichiara una sealed trait DBType, è possibile abbinare modello su di esso con esaustività controllando (per esempio, Scala ti dirà se dimentichi un caso).

In ogni caso, non mi piace Enumeration di Scala, e sono difficilmente solo in quella. Io non uso mai, e se c'è qualcosa per cui enumerazione è davvero la soluzione più pulita, è meglio basta scrivere in Java, usando l'enumerazione di Java.

+0

Sono pienamente d'accordo. Le enumerazioni di Scala sono assolutamente inutili. Forniscono solo l'auto-generazione di valori sequenziali, di cui dubito che qualcuno abbia bisogno. Contrariamente, non v'è alcun buon modo per cercare un valore da stringa id (riflessione è impiegato sotto) e non esiste un modo legale per risolvere enumerazione da enumerazione # Valore. –

4

Andrei per la variante singleton, in quanto consente una maggiore sottoclassi.

Inoltre potrebbe essere necessario fare specifiche db cose/override, dal momento che alcune query/subqueries/operatori potrebbero essere diversi.

ma mi piacerebbe provare qualcosa di simile:

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 
    } 

} 

se questo ha aiutato, perche a +1 questo :)

+0

tratti non possono avere parametri. Doveva essere una classe astratta? –

+0

si dispiace, ho appena copiato il codice e dimenticato il trait lì. –

+0

Vuoi dire che hai copiato il codice di mojojojo. –

10

Per questo particolare caso in cui non è realmente necessario classi per ogni tipo di database; sono solo dati A meno che il caso reale è drammaticamente più complesso, vorrei usare una soluzione a base di analisi mappa e la stringa di ridurre al minimo la quantità di duplicazione del codice:

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