Let there a few separate DAO classes OrderDAO
, ProductDAO
, and CustomerDAO
that store/retrieve data in the database and share a single 开发者_高级运维instance DataSource
(the database connection factory).
In order to create a DataSource
instance and plug it in DAOs
we usually use Spring DI. Now I would like to do that in Scala without any DI framework.
I've read about the cake pattern, and it looks like I should do the following:
trait DatabaseContext { val dataSource:Datasource }
trait OrderDAO {this:DatabaseContext =>
... // use dataSource of DatabaseContext
}
trait ProductDAO {this:DatabaseContext =>
... // use dataSource of DatabaseContext
}
object DAOImpl extends OrderDAO with ProductDAO with DatabaseContext {
val dataSource = ... // init the data source
}
Do I understand the cake pattern correctly?
Can I implement these DAOs
differently using the cake pattern ?
What does it provide that DI frameworks like Spring do not ?
How can I create separate OrderDAOImpl
and ProductDAOImpl
objects sharing the same DataSource
instance instead of one big DAOImpl
?
The advantages of the cake pattern are:
- Unlike configuration-file-based DI solutions, matching contracts to implementations is done at compile time, which reduces class-finding and compatibility issues. However, many DI engines have an alternative in-code configuration feature
- No third-party libraries are used. Self-type annotations which let you use the pattern are a native language feature. No special syntax is used to retrieve the implementation of the contract
- Forgetting to specify an implementation for a component needed by another component results in a runtime error - just check this article http://jonasboner.com/2008/10/06/real-world-scala-dependency-injection-di.html and try not specifying one of the components or specifying a trait instead of a concrete class in any of the cake pattern examples or even forgetting to initialize a val corresponding to a component needed
However, to experience these advantages, you need to more strictly adhere to the architecture of the pattern - check the same article and note the wrapping traits that contain the actual contracts and implementations.
Your examples do not seem to be strictly the cake pattern. In your case you could've just used inheritance to create implementations for your traits and use separate classes for each DAO component. In the cake pattern the consuming code would be a component just like the DAO code, and the code assembling the dependencies together would stand alone from it.
To illustrate the cake pattern, you would have to add consuming classes (domain layer or UI layer) to your example. Or it the case your DAO components accessed each other's features you could illustrate the cake pattern on you DAO alone.
to make it short,
trait OrderDAOComponent {
val dao: OrderDAO
trait OrderDAO {
def create: Order
def delete(id: Int): Unit
//etc
}
}
trait OrderDAOComponentImpl extends OrderDAOComponent {
class OrderDAOJDBCImpl extends OrderDAO {
def create: Order = {/*JDBC-related code here*/}
def delete(id: Int) {/*JDBC-related code here*/}
//etc
}
}
//This one has a dependency
trait OrderWebUIComponentImpl {
this: OrderDAOComponent =>
class OrderWebUI {
def ajaxDelete(request:HttpRequest) = {
val id = request.params("id").toInt
try {
dao.delete(id)
200
}
catch {
case _ => 500
}
}
}
}
//This matches contracts to implementations
object ComponentRegistry extends
OrderDAOComponentImpl with
OrderWebUIComponentImpl
{
val dao = new OrderDAOJDBCImpl
val ui = new OrderWebUI
}
//from some front-end code
val status = ComponentRegistry.ui.ajaxDelete(request)
More on your example. I think it could be more like cake if:
trait DatabaseContext { val dataSource:Datasource }
trait OrderDAOComponent {this:DatabaseContext =>
trait OrderDAOImpl {
... // use dataSource of DatabaseContext
}
}
trait ProductDAOComponent {this:DatabaseContext =>
trait ProductDAOImpl {
... // use dataSource of DatabaseContext
}
}
object Registry extends OrderDAOComponent with ProductDAOComponent with DatabaseContextImpl {
val dataSource = new DatasourceImpl //if Datasource is a custom trait, otherwise wrapping needed
val orderDAO = new OrderDAOImpl
val productDAO = new ProductDAOImpl
}
//now you may use them separately
Registry.orderDAO.//
Maybe:
- Statically checked at compile time.
精彩评论