Le propos de cet article n'est pas de lister et de comparer toutes les méthodes disponibles pour ce faire; ceci sera traité dans un article ultérieur; mais de décrire une manière d'injecter les dépendances dans un contexte précis.
J'ai récemment eu a participer au développement d'une application web "classique" avec des Dao et des Services.
Ces Dao et Services sont naturellement indiqués pour être utilisés comme des singletons. En Scala, nous disposons d'un mot-clé pour déclarer des singletons : object.
Je vais donc essayer d'illustrer comment injecter une dépendance dans un object Scala et de rendre cet object lui même injectable dans une classe tierce.
Le code
J'utilise un exemple simpliste pour illustrer la méthode d'injection de dépendance. J'ai un objet métier Boat, et je veux créer un repository BoatRepository. J'ai besoin d'injecter dans ce repository une instance de type DB qui me permettra d'utiliser l'api d'un quelconque driver (par exemple un driver mongodb).
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// The "classic" Repository interface | |
trait BoatRepository { | |
def find(uid: String): Future[Boat] | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// an abstract declaration of DB instance for the repository | |
trait Database { | |
protected def db: DB | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// For example use a MongoDb instance dao for insert Boat | |
trait MongoDbBoatDao extends BoatRepository with Database { | |
def find(uid: String) = db.find(uid) // here code the Dao implementation | |
} | |
// Here we construct the Scala object Dao using trait mixin | |
object MongoDbBoatDao extends MongoDbBoatDao with ProdDatabase |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// with this example test we can show how overload ProdDatabase instance | |
class MongoDbBoatDaoTest extends FunSuite { | |
val connection: DB = { | |
// create a DB instance test | |
} | |
// override db with test db instance to perform testing | |
trait TestDbInjection extends Database { val db = connection } | |
object UnderTestDao extends MongoDbBoatDao with TestDbInjection | |
test("should insert boat") { | |
UnderTestDao.insert(...) | |
// test something ... | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// This trait produce a concrete implementation of DB instance to inject into Dao | |
trait ProdDatabase extends Database { | |
protected val db = FactoryDb.init() // a example way of retrieve a DB instance | |
} |
L'explication
La méthode est assez simple et le code assez compréhensible par lui-même. Si je résume :- Utiliser une interface abstraite (un trait) pour déclarer les méthodes de mon repository (BoatRepository)
- Implémenter ce repository dans un trait en déclarant une dépendance vers une base de type DB (MongoDbBoatDao), cette dépendance étant elle même déclarée dans un trait (Database)
- Fournir une implémentation à cette Database (ProdDatabase)
- Construire le singleton Dao en mixant les trait MongoDbBoatDao et ProdDatabase
- Pour surcharger la dépendance du singleton, par exemple dans un test (MongoDbBoatDaoTest), il suffit de mixer le trait MongoDbBoatDao avec un autre trait surchargeant le membre DB
A noter aussi que ce système est "scalable", dans le sens ou si je veux injecter le DAO dans un autre singleton, dans un service par exemple, il me suffit de faire la même chose où le DAO prendrait la place de la DB et le service celui du DAO.
Cool non ? :)
ressource
Aucun commentaire:
Enregistrer un commentaire