首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >动态更改持久化层(在运行时),尽可能少的更改

动态更改持久化层(在运行时),尽可能少的更改
EN

Stack Overflow用户
提问于 2015-08-31 20:26:06
回答 5查看 415关注 0票数 3

我正在寻找一种设计模式/方法来动态地交换我的应用程序的(持久性)层(最好是在运行时)。

为什么?

我希望能够决定是否将某些数据保存到XML或“每个实例”-basis上的数据库中。因此,我可能决定一个项目使用XML作为后端,另一个项目使用数据库。我希望在这里变得灵活,并且能够轻松地添加另一个“驱动程序”,例如Json或其他什么。

现在假设以下设置:

我们有一个控制器,我们想管理一些数据。我们可以在SQL和XML实现之间进行选择。

一种可能的(有效)解决办法:

BasicController.scala

代码语言:javascript
复制
val myPersistenceLayer: PersistenceLayer = SQLPersistenceLayer

val apples: Seq[Apple] = myPersistenceLayer.getApples()

trait PersistenceLayer
{
    def getApples(): Seq[Apple]
    def getBananas(): Seq[Banana]
}

object SQLPersistenceLayer extends PersistenceLayer
{
    override def getApples(): Seq[Apple] = {...}
    override def getBananas(): Seq[Banana] = {...}
}

这是一个相当麻烦的解决方案,因为我们必须为每个新的模型添加方法(想想结果!;),不仅在特性上,而且在每个实现中。我喜欢我的单一责任,所以我宁愿把它委托给模特,比如:

代码语言:javascript
复制
trait PersistenceLayer
{
    def getAll(model: Model): Seq[Model] = { model.getAll() }
}

trait Model
{
    def getAll(): Seq[Model]
}

package "SQL"

class Apple extends Model
{
    def getAll(): Seq[Apple] = { // do some SQL magic here }
}

package "XML"

class Apple extends Model
{
    def getAll(): Seq[Apple] = { // do some XML magic here instead }
}

现在最大的问题是,即使我实现了一个具体的PersistenceLayer,如下所示:

代码语言:javascript
复制
object SQLPersistenceLayer extends PersistenceLayer {}

我如何告诉应用程序使用正确包的模型?

如果我使用SQLPersistenceLayer:

代码语言:javascript
复制
val apples = myPersistenceLayer.get(Apple) 

我需要导入正确的"Apple“类,这违背了整个目的,因为这样我就可以删除所有其他类,导入正确的类,并在其上使用通用的"getAll()”方法。

因此,我再次需要更改多行的实现,这正是我想要避免的。

我想到了一些东西,比如给出一个带有包名的字符串,比如

val package = "sql“,并在控制器中从正确的包中导入它,但是这并不是真正可行的,也不太容易实现,对于我显然缺少的东西来说,这是一个相当讨厌的攻击。

长话短说:我希望能够切换软件包,用于我的持久性需要的dynamically. 在一些动态类型化语言中,我可以想出一个解决方案,但不是用Scala或任何静态类型化语言,所以我想我不知道这里的某个设计模式()

编辑**

出现了一个想法(是的,有时会发生;),现在我想知道这样的事情是否会导致我想要的结果:

代码语言:javascript
复制
namespace tld.app.persistence

trait PersistenceLayer
{
    proteced val models: mutable.HashMap[String, Model] = new mutable.HashMap[String, Model]

    def registerModel(key: String, model: Model): Unit =
    {
        models.remove(key)
        models.put(key, model)
    }

    def get(model: String): Seq[Future[Model]] =
    {
        val m: Model = models.getOrElse(model, throw new Exception("No such model found!"))
        m.get
    }   
}

trait Model
{
    def get(): Seq[Future[Model]]
}

namespace tld.app.persistence.sql

object SQLPersistenceLayer extends PersistenceLayer

class Person extends Model
{
    def get(): Seq[Future[Model]] =
    {
        // ... query the database
    }
}

namespace tld.app.persistence.xml

object XMLPersistenceLayer extends PersistenceLayer

class Person extends Model
{
    def get(): Seq[Future[Model]] =
    {
        // ... read in from the appropriate xml-file
    }
}

object Settings
{
    var persistenceLayer: PersistenceLayer = SQLPersistenceLayer // Default is SQLPersistenceLayer
}

Somewhere in the application:

Settings.persistenceLayer.get("person")

// Then a user-interaction happens

Settings.persistenceLayer = XMLPersistenceLayer

Settings.persistenceLayer.get("person")

persistenceLayer通常保持不变,但用户可以决定是否更改它。只要我能找到时间,我就会更深入地研究它。但也许有人马上就发现了这种方法的问题。

EN

回答 5

Stack Overflow用户

发布于 2015-09-01 03:36:25

DI允许您在编译时连接一个实现。Scala中有很多方法可以实现DI (蛋糕模式、Reader、DI框架等)。

如果您想在应用程序启动时连接依赖项,那么常规依赖机制就可以工作了。您只需根据某些条件创建一个所需依赖项(SQL,XML)的实例,并将其传递给代码。

如果您希望在应用程序执行过程中保持在依赖项之间的切换,即有时保存为SQL,有时保存为XML,那么您可以使用类似于升力喷射器的东西,请参见我的答案这里 -选项2。

票数 2
EN

Stack Overflow用户

发布于 2015-09-01 12:07:58

您可以使用运行时反射来完成它。您需要在运行时指定和创建类/对象,然后将其传递到持久性层,然后只调用泛型getAll方法。

有关反射库-> http://docs.scala-lang.org/overviews/reflection/overview.html的详细信息

最好使具有getAll方法的伙伴对象getAll针对每个持久化层进行不同的实现。

然后使用完整的包名访问带有反射的Apple对象

代码语言:javascript
复制
val apple:sql.Apple = //Reflection library object access
val apple:xml.Apple = //Reflection library object access


val apples = myPersistenceLayer.get(apple)
票数 1
EN

Stack Overflow用户

发布于 2015-09-01 18:01:31

我认为您可以通过执行以下操作来实现基于模块的包含,如inclusion + TypeTags。

代码语言:javascript
复制
object SqlPersistence {
  implicit def getAll[T: TypeTag](): Seq[T] = {/* type-based sql implementation*/}
}

object JsonPersistence {
  implicit def getAll[T: TypeTag](): Seq[T] = {/* type-based json implementation*/}
}

object PersistenceLayer {
  def getAll[T](implicit getter: Unit => Seq[T]): Seq[T] = getter
}

// somewhere else ...
import SqlPersistence._

PersistenceLayer.getAll[Apple]

优点是您可以通过引入相应的导入来决定您的持久化层。主要的缺点是一样的:您需要通过每次调用来决定您的持久性层,并确保它是您所想的。此外,从我个人的经验来看,编译器对复杂的隐式角落案例帮助不大,因此有可能花费更多的时间进行调试。

如果您为应用程序设置了一次持久层,那么DI就会做得很好,例如蛋糕图案。但是,您要么需要每个类都有一个方法,要么就需要使用反射。在没有反射的情况下,情况可能是这样的:

代码语言:javascript
复制
trait PersistenceLayer {
  def getApples(): Apples
}

trait SqlPersistenceLayer extends PersistenceLayer {
  override def getApples() = // sql to get apples 
}

trait Controller {
  this: PersistenceLayer =>

  def doMyAppleStuff = getApples()
}

// somewhere in the main ...
val controller = new Controller with SqlPersistence {}
controller.doMyAppleStuff
票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/32319416

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档