Scala 蛋糕模式和自我类型注释
Scala Cake Pattern & Self Type Annotations
我正在尝试遵循此 blog 中的示例。我理解该示例,但在实施时遇到问题。
trait Database {
// ...
}
trait UserDb {
this: Database =>
// ...
}
trait EmailService {
this: UserDb =>
// Can only access UserDb methods, cannot touch Database methods
}
该示例提到完整的数据库功能将从 EmailService 中隐藏 - 这就是我所追求的,但不知道如何正确实现这些特征。
This是我试图实现的:
trait Database {
def find(query: String): String
}
trait UserDb {
this: Database =>
}
trait EmailService {
this: UserDb =>
}
trait MongoDatabase extends Database {
}
trait MongoUserDb extends UserDb with MongoDatabase{
}
class EmailServiceImpl extends EmailService with MongoUserDb {
override def find(query: String): String = {
"result"
}
}
我觉得这很奇怪,因为 MongoDatabase 特性没有要求 find
实现,当我实现 EmailService
时,系统提示我进行 find
实现,尽管这个例子提到了这个将从 EmailService
中隐藏。我在这里错过了什么?
阅读您的评论后,我正在尝试通过一个更接近我实际尝试做的示例来实现我正在尝试理解的内容。
第一个片段无法编译,但第二个片段会...
在一天结束时,我想要不同的 Repository
实现,我可以在它们依赖的数据库之间切换,我是否以下面的片段之一结束?
trait Database {
def find(s: String): String
}
trait Repository {
this: Database =>
}
class UserRepository extends Repository {
def database = new MongoDB
class MongoDB extends Database {
def find(s: String): String = {
"res"
}
}
}
trait Repository {
def database: Database
trait Database {
def find(s: String): String
}
}
trait UserRepository extends Repository {
def database = new MongoDB
class MongoDB extends Database {
def find(s: String): String = {
"res"
}
}
}
Database
对 EnailService
隐藏,但对 EmailServiceImpl
不隐藏。后者是MongoUserDB
的子类,显然,它可以访问它。
MongoUserDB
不会 "ask" 用于 find
实现,因为它是一个特征,并且特征可以有抽象方法。你仍然应该在那里实现它,即使没有被问到 ;)
如前所述,MongoUserDB
不会要求实现,因为它是 trait
。但是,由于 EmailServiceImpl
extends
它需要提供实现的特征。
您正在寻找的可以通过添加另一个抽象来完成。我使用 service
和 DAO
架构来实现。
下面是一个工作示例,您可以使用它来查看它是否适合您。
//All future versions of DAO will extend this
trait AbstractDAO{
def getRecords:String
def updateRecords(records:String):Unit
}
//One concrete version
trait concreteDAO extends AbstractDAO{
override def getRecords={"Here are DB records"}
override def updateRecords(records:String){
//Actual DB calls and operations
println("Updated "+records)
}
}
//Second concrete version
trait concreteDAO1 extends AbstractDAO{
override def getRecords={"DB Records returned from DAO2"}
override def updateRecords(records:String){
//Actual DB calls and operations
println("Updated via DAO2"+records)
}
}
//This trait just defines dependencies (in this case an instance of AbstractDAO) and defines operations based over that
trait service{
this:AbstractDAO =>
def updateRecordsViaDAO(record:String)={
updateRecords(record)
}
def getRecordsViaDAO={
getRecords
}
}
//Test Stub
object DI extends App{
val wiredObject = new service with concreteDAO //injecting concrete DAO to the service and calling methods
wiredObject.updateRecords("RECORD1")
println(wiredObject.getRecords)
val wiredObject1 = new service with concreteDAO1
wiredObject1.updateRecords("RECORD2")
println(wiredObject1.getRecords)
}
编辑 ---
这是您可能想要实现的代码,
trait Database {
def find(s: String): String
}
trait MongoDB extends Database{
def find(s:String):String = { "Test String" }
}
trait SQLServerDB extends Database{
def find(s:String):String = { "Test String2" }
}
trait Repository {
this: Database =>
}
class UserRepository extends Repository with MongoDB{ // UserRepository is injected with MongoDB here
find("call MongoDB") //This call will go to the find method in MongoDB trait
}
class UserRepository1 extends Repository with SQLServerDB{ // UserRepository is injected with SQLServerDB here
find("call SQLServerDB") //This call will go to the find method in SQLServerDB trait
}
我正在尝试遵循此 blog 中的示例。我理解该示例,但在实施时遇到问题。
trait Database {
// ...
}
trait UserDb {
this: Database =>
// ...
}
trait EmailService {
this: UserDb =>
// Can only access UserDb methods, cannot touch Database methods
}
该示例提到完整的数据库功能将从 EmailService 中隐藏 - 这就是我所追求的,但不知道如何正确实现这些特征。
This是我试图实现的:
trait Database {
def find(query: String): String
}
trait UserDb {
this: Database =>
}
trait EmailService {
this: UserDb =>
}
trait MongoDatabase extends Database {
}
trait MongoUserDb extends UserDb with MongoDatabase{
}
class EmailServiceImpl extends EmailService with MongoUserDb {
override def find(query: String): String = {
"result"
}
}
我觉得这很奇怪,因为 MongoDatabase 特性没有要求 find
实现,当我实现 EmailService
时,系统提示我进行 find
实现,尽管这个例子提到了这个将从 EmailService
中隐藏。我在这里错过了什么?
阅读您的评论后,我正在尝试通过一个更接近我实际尝试做的示例来实现我正在尝试理解的内容。
第一个片段无法编译,但第二个片段会...
在一天结束时,我想要不同的 Repository
实现,我可以在它们依赖的数据库之间切换,我是否以下面的片段之一结束?
trait Database {
def find(s: String): String
}
trait Repository {
this: Database =>
}
class UserRepository extends Repository {
def database = new MongoDB
class MongoDB extends Database {
def find(s: String): String = {
"res"
}
}
}
trait Repository {
def database: Database
trait Database {
def find(s: String): String
}
}
trait UserRepository extends Repository {
def database = new MongoDB
class MongoDB extends Database {
def find(s: String): String = {
"res"
}
}
}
Database
对 EnailService
隐藏,但对 EmailServiceImpl
不隐藏。后者是MongoUserDB
的子类,显然,它可以访问它。
MongoUserDB
不会 "ask" 用于 find
实现,因为它是一个特征,并且特征可以有抽象方法。你仍然应该在那里实现它,即使没有被问到 ;)
如前所述,MongoUserDB
不会要求实现,因为它是 trait
。但是,由于 EmailServiceImpl
extends
它需要提供实现的特征。
您正在寻找的可以通过添加另一个抽象来完成。我使用 service
和 DAO
架构来实现。
下面是一个工作示例,您可以使用它来查看它是否适合您。
//All future versions of DAO will extend this
trait AbstractDAO{
def getRecords:String
def updateRecords(records:String):Unit
}
//One concrete version
trait concreteDAO extends AbstractDAO{
override def getRecords={"Here are DB records"}
override def updateRecords(records:String){
//Actual DB calls and operations
println("Updated "+records)
}
}
//Second concrete version
trait concreteDAO1 extends AbstractDAO{
override def getRecords={"DB Records returned from DAO2"}
override def updateRecords(records:String){
//Actual DB calls and operations
println("Updated via DAO2"+records)
}
}
//This trait just defines dependencies (in this case an instance of AbstractDAO) and defines operations based over that
trait service{
this:AbstractDAO =>
def updateRecordsViaDAO(record:String)={
updateRecords(record)
}
def getRecordsViaDAO={
getRecords
}
}
//Test Stub
object DI extends App{
val wiredObject = new service with concreteDAO //injecting concrete DAO to the service and calling methods
wiredObject.updateRecords("RECORD1")
println(wiredObject.getRecords)
val wiredObject1 = new service with concreteDAO1
wiredObject1.updateRecords("RECORD2")
println(wiredObject1.getRecords)
}
编辑 ---
这是您可能想要实现的代码,
trait Database {
def find(s: String): String
}
trait MongoDB extends Database{
def find(s:String):String = { "Test String" }
}
trait SQLServerDB extends Database{
def find(s:String):String = { "Test String2" }
}
trait Repository {
this: Database =>
}
class UserRepository extends Repository with MongoDB{ // UserRepository is injected with MongoDB here
find("call MongoDB") //This call will go to the find method in MongoDB trait
}
class UserRepository1 extends Repository with SQLServerDB{ // UserRepository is injected with SQLServerDB here
find("call SQLServerDB") //This call will go to the find method in SQLServerDB trait
}