在不使用var的情况下,如何在Scala中使静态值无效并在以后再次将其设置为新值?

如何解决在不使用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来使连接无效,并在再次建立数据库连接后将其设置为新值。

创建这种逻辑的功能方式是什么?我需要连接的静态值,以便仅创建一个连接,并且我想要一种方法来检查该值是否过时。

解决方法

可变性通常是不可避免的,因为它是我们构建的系统的固有属性。但是,这并不意味着我们必须在代码中使用可变变量。

通常有两种主要方法可以处理涉及可变状态的情况:

  1. 将可变状态推到程序外部的存储库中。

典型的例子是“标准”数据库(如果状态需要保留)和内存存储(如果状态在程序生命周期中一直存在)。每当您从此类存储中获取值时,都将其视为不可变值。可变性仍然存在,但是在您的程序中却不存在,这使得推理变得更容易。

有人批评这种思维方式,说“您什么都没解决,只是在解决其他问题”,实际上是对的。我们让数据库为我们处理可变性。为什么不?这就是数据库的设计目的。此外,可变性的主要问题是它的推理,而我们也不会推理数据库的内部实现。因此,将可变性从我们的一项服务推向另一项服务的确就像在扔土豆一样,但是将其推向为其设计的外部系统是完全可以的。

但是,所有这些对您的情况无济于事,因为将数据库连接对象存储在外部存储中并不是很优雅。我需要指出第二点。

  1. 使用状态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 举报,一经查实,本站将立刻删除。

相关推荐


依赖报错 idea导入项目后依赖报错,解决方案:https://blog.csdn.net/weixin_42420249/article/details/81191861 依赖版本报错:更换其他版本 无法下载依赖可参考:https://blog.csdn.net/weixin_42628809/a
错误1:代码生成器依赖和mybatis依赖冲突 启动项目时报错如下 2021-12-03 13:33:33.927 ERROR 7228 [ main] o.s.b.d.LoggingFailureAnalysisReporter : *************************** APPL
错误1:gradle项目控制台输出为乱码 # 解决方案:https://blog.csdn.net/weixin_43501566/article/details/112482302 # 在gradle-wrapper.properties 添加以下内容 org.gradle.jvmargs=-Df
错误还原:在查询的过程中,传入的workType为0时,该条件不起作用 &lt;select id=&quot;xxx&quot;&gt; SELECT di.id, di.name, di.work_type, di.updated... &lt;where&gt; &lt;if test=&qu
报错如下,gcc版本太低 ^ server.c:5346:31: 错误:‘struct redisServer’没有名为‘server_cpulist’的成员 redisSetCpuAffinity(server.server_cpulist); ^ server.c: 在函数‘hasActiveC
解决方案1 1、改项目中.idea/workspace.xml配置文件,增加dynamic.classpath参数 2、搜索PropertiesComponent,添加如下 &lt;property name=&quot;dynamic.classpath&quot; value=&quot;tru
删除根组件app.vue中的默认代码后报错:Module Error (from ./node_modules/eslint-loader/index.js): 解决方案:关闭ESlint代码检测,在项目根目录创建vue.config.js,在文件中添加 module.exports = { lin
查看spark默认的python版本 [root@master day27]# pyspark /home/software/spark-2.3.4-bin-hadoop2.7/conf/spark-env.sh: line 2: /usr/local/hadoop/bin/hadoop: No s
使用本地python环境可以成功执行 import pandas as pd import matplotlib.pyplot as plt # 设置字体 plt.rcParams[&#39;font.sans-serif&#39;] = [&#39;SimHei&#39;] # 能正确显示负号 p
错误1:Request method ‘DELETE‘ not supported 错误还原:controller层有一个接口,访问该接口时报错:Request method ‘DELETE‘ not supported 错误原因:没有接收到前端传入的参数,修改为如下 参考 错误2:cannot r
错误1:启动docker镜像时报错:Error response from daemon: driver failed programming external connectivity on endpoint quirky_allen 解决方法:重启docker -&gt; systemctl r
错误1:private field ‘xxx‘ is never assigned 按Altʾnter快捷键,选择第2项 参考:https://blog.csdn.net/shi_hong_fei_hei/article/details/88814070 错误2:启动时报错,不能找到主启动类 #
报错如下,通过源不能下载,最后警告pip需升级版本 Requirement already satisfied: pip in c:\users\ychen\appdata\local\programs\python\python310\lib\site-packages (22.0.4) Coll
错误1:maven打包报错 错误还原:使用maven打包项目时报错如下 [ERROR] Failed to execute goal org.apache.maven.plugins:maven-resources-plugin:3.2.0:resources (default-resources)
错误1:服务调用时报错 服务消费者模块assess通过openFeign调用服务提供者模块hires 如下为服务提供者模块hires的控制层接口 @RestController @RequestMapping(&quot;/hires&quot;) public class FeignControl
错误1:运行项目后报如下错误 解决方案 报错2:Failed to execute goal org.apache.maven.plugins:maven-compiler-plugin:3.8.1:compile (default-compile) on project sb 解决方案:在pom.
参考 错误原因 过滤器或拦截器在生效时,redisTemplate还没有注入 解决方案:在注入容器时就生效 @Component //项目运行时就注入Spring容器 public class RedisBean { @Resource private RedisTemplate&lt;String
使用vite构建项目报错 C:\Users\ychen\work&gt;npm init @vitejs/app @vitejs/create-app is deprecated, use npm init vite instead C:\Users\ychen\AppData\Local\npm-