Лучшие практики Scala: наследование признаков и перечисление - PullRequest
11 голосов
/ 09 октября 2011

В настоящее время я экспериментирую со 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 ]

12 голосов
/ 09 октября 2011

Если вы объявляете sealed trait DBType, вы можете сопоставить с ним шаблон с проверкой исчерпывающего характера (т. Е. Scala сообщит вам, если вы забудете один случай).

Во всяком случае, мне не нравится Enumeration Скалы, и я вряд ли одинок в этом. Я никогда не использую его, и если есть что-то, для чего перечисление является действительно самым чистым решением, лучше просто написать это на Java, используя перечисление Java.

10 голосов
/ 09 октября 2011

Для этого конкретного случая вам не нужны классы для каждого типа базы данных;это просто данные.Если реальный случай не является значительно более сложным, я бы использовал решение на основе анализа карт и строк, чтобы минимизировать количество дублирования кода:

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))
  }
}
4 голосов
/ 09 октября 2011

Я бы пошел на одноэлементный вариант, так как он позволяет более четкие подклассы.

Также вам может потребоваться выполнить специфичные для БД вещи / переопределения, поскольку некоторые запросы / подзапросы / операторы могут отличаться.

Но я бы попробовал что-то вроде этого:

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

...