如何解决在不使用var的情况下,如何在Scala中使静态值无效并在以后再次将其设置为新值?
我已经建立了工厂方法,该方法应该启动数据库(cassandra
)并连接到数据库,或者应该返回现有会话。与数据库的连接是静态字段。
class EmbeddedCassandraManager {
def getCassandra() = {
if(EmbeddedCassandraManager.cassandraConnection.isDefined) //return existing instance
{
(EmbeddedCassandraManager.testCassandra,EmbeddedCassandraManager.cassandraConnection)
}
else {
EmbeddedCassandraManager.startCassandra()
}
}
def closeCassandra() = {
EmbeddedCassandraManager.closeCassandra()
}
}
object EmbeddedCassandraManager {
val factory = new EmbeddedCassandraFactory
//can I do the logic without using var?
var (testCassandra,cassandraConnection) = startCassandra()
def closeCassandra() = {
cassandraConnection.get.close()
cassandraConnection = None
testCassandra.stop()
}
def startCassandra():(Cassandra,Option[CassandraConnection]) = {
val testCassandra = factory.create()
testCassandra.start()
val cassandraConnectionFactory:DefaultCassandraConnectionFactory = new DefaultCassandraConnectionFactory();
val localCassandraConnection:Option[CassandraConnection] = try{
val connection = cassandraConnectionFactory.create(testCassandra)
Some(connection)
}catch{
case exception:Throwable => {
throw exception
}
}
this.cassandraConnection = localCassandraConnection
(testCassandra,this.cassandraConnection)
}
}
我能够创建逻辑的唯一方法是对var
使用cassandraConnection
。有没有可以避免使用var
的模式?
在一项测试中,我必须停止cassandra
以测试如果数据库未运行则连接未建立。这会使现有的连接失效。如果没有var
,我将无法将值设置为None
来使连接无效,并在再次建立数据库连接后将其设置为新值。
创建这种逻辑的功能方式是什么?我需要连接的静态值,以便仅创建一个连接,并且我想要一种方法来检查该值是否过时。
解决方法
可变性通常是不可避免的,因为它是我们构建的系统的固有属性。但是,这并不意味着我们必须在代码中使用可变变量。
通常有两种主要方法可以处理涉及可变状态的情况:
- 将可变状态推到程序外部的存储库中。
典型的例子是“标准”数据库(如果状态需要保留)和内存存储(如果状态在程序生命周期中一直存在)。每当您从此类存储中获取值时,都将其视为不可变值。可变性仍然存在,但是在您的程序中却不存在,这使得推理变得更容易。
有人批评这种思维方式,说“您什么都没解决,只是在解决其他问题”,实际上是对的。我们让数据库为我们处理可变性。为什么不?这就是数据库的设计目的。此外,可变性的主要问题是它的推理,而我们也不会推理数据库的内部实现。因此,将可变性从我们的一项服务推向另一项服务的确就像在扔土豆一样,但是将其推向为其设计的外部系统是完全可以的。
但是,所有这些对您的情况无济于事,因为将数据库连接对象存储在外部存储中并不是很优雅。我需要指出第二点。
- 使用状态monad。
如果“ monad”一词为您带来一些麻烦,请假装我说“ use State
”(实际上,这是一个简单的概念,不需要大词)。我将使用Cats library中可用的State
的实现,但它也存在于其他FP库中。
State
是一个从某些现有状态到新状态以及某些产生值的函数:
S => (S,V)
通过从现有状态到新状态,我们实现了“状态突变”。
示例1:
下面是一些使用整数状态的代码,该状态每次递增1并在状态每次变化时产生一个字符串值:
import cats.data.State
val s: State[Int,String] = State((i: Int) => (i + 1,s"Value: $i"))
val program = for {
produced1 <- s
_ = println(produced1) // Value: 42
produced2 <- s
_ = println(produced2) // Value: 43
produced3 <- s
_ = println(produced3) // Value: 44
} yield "Done."
program.run(42).value
这就是要点。
示例2:
为完整起见,这是一个更大的示例,演示了与您的用例相似的用例。
首先,让我们介绍一个CassandraConnection
的简化模型(这只是为了示例;实际对象将来自Cassandra库,因此我们自己的代码中将不存在任何可变性)。
class CassandraConnection() {
var isOpen: Boolean = false
def connect(): Unit = isOpen = true
def close(): Unit = isOpen = false
}
我们应该如何定义状态?可变对象显然是CassandraConnection
,用于理解的结果值可以是简单的String
。
import cats.data.State
type DbState = State[CassandraConnection,String]
现在,让我们定义一些用于使用现有CassandraConnection
对象操纵状态的函数。
val openConnection: DbState = State(connection => {
if (connection.isOpen) {
(connection,"Already connected.")
} else {
val newConnection = new CassandraConnection()
newConnection.connect()
(newConnection,"Connected!")
}
})
val closeConnection: DbState = State(connection => {
connection.close()
(connection,"Closed!")
})
val checkConnection: DbState =
State(connection => {
if (connection.isOpen) (connection,"Connection is open.")
else (connection,"Connection is closed.")
})
最后,让我们在主程序中使用以下功能:
val program: DbState =
for {
log1 <- checkConnection
_ = println(log1) // Connection is closed.
log2 <- openConnection
_ = println(log2) // Connected!
log3 <- checkConnection
_ = println(log3) // Connection is open.
log4 <- openConnection
_ = println(log4) // Already connected.
log5 <- closeConnection
_ = println(log5) // Closed!
log6 <- checkConnection
_ = println(log6) // Connection is closed.
} yield "Done."
program.run(new CassandraConnection()).value
我知道这不是可以复制/粘贴到项目中并使其正常工作的确切代码,但我想给出一个更通用的答案,对于其他读者来说可能更容易理解。通过一些尝试,我相信您可以将其塑造成自己的解决方案。只要您的主程序是State
级别的理解,您就可以轻松地打开和关闭连接并(重新)使用相同的连接对象。
此解决方案真正实现了什么?为什么这比仅具有可变的CassandraConnection
值更好?
一件大事是我们实现了参照透明性,这就是为什么这种模式很好地适合于函数式编程范例,而标准可变性却不适用的原因。由于这个答案已经有点长了,我将向您指出Cats documentation,它会更详细地说明整个过程,并很好地展示了使用State
的好处。
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。