2011-10-08 37 views
11

Tôi hiện đang thử nghiệm với Scala và tìm kiếm các phương pháp hay nhất. Tôi thấy mình có hai cách đối lập để giải quyết một vấn đề duy nhất. Tôi muốn biết cái nào tốt hơn và tại sao, đó là thông thường hơn, và nếu có thể bạn biết một số cách tiếp cận tốt hơn khác. Cái thứ hai trông đẹp hơn với tôi.Thực tiễn tốt nhất của Scala: Thừa kế thừa kế và liệt kê

1. Liệt kê dựa trên giải pháp

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. Giải pháp Singleton dựa trên

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

Trả lời

12

Nếu bạn khai báo một sealed trait DBType, bạn có thể mô hình phù hợp trên nó với exhaustiveness kiểm tra (ví dụ, Scala sẽ cho bạn biết nếu bạn quên một trường hợp).

Dù sao, tôi không thích Scala's Enumeration và tôi hầu như không đơn độc trong đó. Tôi không bao giờ sử dụng nó, và nếu có một cái gì đó mà liệt kê thực sự là giải pháp sạch nhất, tốt hơn là chỉ cần viết nó trong Java, sử dụng liệt kê của Java.

+0

Tôi hoàn toàn đồng ý. Scala liệt kê là hoàn toàn vô dụng. Họ chỉ cung cấp tự động tạo ra các giá trị tuần tự, mà tôi nghi ngờ ai đó cần chút nào. Ngược lại, không có cách nào tốt để tìm kiếm một giá trị bằng id chuỗi (phản ánh được sử dụng bên dưới) và không có cách hợp pháp để giải quyết Enumeration từ Enumeration # Value. –

4

Tôi muốn đi cho các biến thể singleton, vì nó cho phép subclassing rõ ràng hơn.

Ngoài ra, bạn có thể cần phải thực hiện những điều/ghi đè cụ thể cho db, vì một số truy vấn/truy vấn con/toán tử có thể khác nhau.

Nhưng tôi muốn thử một cái gì đó như thế này:

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 
    } 

} 

nếu điều này giúp, xin vui lòng xem xét để +1 mục này :)

+0

Đặc điểm không thể có thông số. Đó có phải là một lớp trừu tượng không? –

+0

vâng xin lỗi, tôi vừa sao chép mã của bạn và quên mất đặc điểm đó. –

+0

Bạn có nghĩa là bạn đã sao chép mã của mojojojo. –

10

Đối với trường hợp này cụ thể trường hợp bạn không thực sự cần các lớp cho từng loại cơ sở dữ liệu; nó chỉ là dữ liệu. Trừ khi trường hợp thực sự phức tạp hơn nhiều, tôi sẽ sử dụng giải pháp dựa trên phân tích cú pháp chuỗi và bản đồ để giảm thiểu số lượng sao chép mã:

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