lundi 31 août 2015

Change persistence layer dynamically (upon runtime) with as few changes as possible

I am searching for a design pattern/way to exchange a (persistence) layer of my application dynamically (preferably even at runtime).

Why?

I'd like to be able to decide whether to save certain data to XML or a database on a "per instance"-basis. So I may decide that one project uses XML as a backend and another uses a database. I want to be flexible here and to be able to easily add another "driver" for e.g. Json or whatever.

Now assume the following setup:

We have a controller and we want to manage some data. We can choose between a SQL and XML implementation.

One possible (working) solution:

BasicController.scala

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] = {...}
}

This is a rather nasty solution as one would have to add methods for each new Model (think fruit! ;)) not only in the trait, but also in every implementation. I like my single responsibility so I'd rather delegate that to the models instead, like:

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

Now the big problem here is, even if I implement a concrete PersistenceLayer, like so:

object SQLPersistenceLayer extends PersistenceLayer {}

how could I tell the application to use the model of the right package?

If I use the SQLPersistenceLayer upon:

val apples = myPersistenceLayer.get(Apple) 

I would need to import the right "Apple" class, which defeats the whole purpose because then I could just remove all other classes, import the right one and just use a generic "getAll()" method on it.

So again I would need to change the implementation at multiple lines, which is what I want to avoid.

I thought about something like giving a string with the package-name, like

val package = "sql" and in the controller to import it from the right package, but this is not really feasible and not really easy to accomplish and it's a rather nasty hack for something I'm obviously missing.

To make a long story short: I want to be able to switch the package to use for my persistence needs dynamically. In some dynamically typed languages I could come up with a solution, but not in Scala or any statically typed language, so I guess I'm not knowing a certain design pattern here

Aucun commentaire:

Enregistrer un commentaire