如何在Scala中进行变量的早期初始化?

问题描述 投票:0回答:3

我有一个体系结构问题,更确切地说,是一个次优的情况。

对于适应性测试环境,存在由一系列定义方法更新的上下文,每种定义方法都定义不同的实体,即更改上下文。 为简单起见,此处的定义将只是整数,而上下文将是不断增长的Seq [Int]。

trait Abstract_Test_Environment {
    def definition(d: Int): Unit
    /* Make definitions: */
    definition(1)
    definition(2)
    definition(3)
}

现在,通过保持当前上下文的连续更改的“ var”来实现此想法:

trait Less_Abstract_Test_Environment extends Abstract_Test_Environment {
    /* Implement the definition framework: */
    var context: Context = initial_context
    val initial_context: Context
    override def definition(d: Int) = context = context :+ d
}

由于必须在应用“定义”之前设置上下文,因此不能通过结论类中的变量分配来设置上下文:

class Concrete_Test_Environment extends Less_Abstract_Test_Environment {
    context = Seq.empty
}

需要一个中间的“ initial_context”,但是一个简单的覆盖也不能完成任务:

class Concrete_Test_Environment extends Less_Abstract_Test_Environment {
    override val initial_context = Seq.empty
}

唯一可行的解​​决方案似乎是早期初始化,这很可能是创建此功能的目的:

class Concrete_Test_Environment extends {
    override val initial_context = Seq.empty
} with Less_Abstract_Test_Environment

但是,我们的设置仍然失败,因为在“ Abstract_Test_Environment”中应用“定义”时,“ Less_Abstract_Test_Environment”中的VAR“上下文”仍未绑定,即为空。 在“ Less_Abstract_Test_Environment”(来自“ Abstract_Test_Environment”)中,定义“ definition”是“按需初始化”的,而变量“ context”则不是。

我想到的“解决方案”是合并“ Abstract_Test_Environment”和“ Less_Abstract_Test_Environment”。 这不是我想要的,因为它破坏了接口/目标与实现之间的自然分离,这是由两个特性实现的。

您看到更好的解决方案了吗? 我相信Scala可以做得更好。

scala module var init
3个回答
1
投票

简单的解决方案:不要在对象创建期间初始化它,除非您处于底层类中。 而是添加一个包含所有初始化代码的init方法,然后在最底层的类(这是安全的,因为已经创建了所有父类)中或在创建对象的任何地方调用它。

整个过程的副作用是,您甚至可以覆盖子类中的初始化代码。


1
投票

一种可能是使您的中级特征成为一类:

abstract class Less_Abstract_Test_Environment(var context: Context = Seq.empty) extends Abstract_Test_Environment {
   override def definition(d: Int) = context = context :+ d
}

您现在可以对其进行子类化,并将不同的初始上下文作为参数传递给构造函数。

如果您希望将中间体作为特征,也可以在“具体”级别进行此操作:

trait Less_Abstract_Test_Environment extends Abstract_Test_Environment {
   var context: Context     
   override def definition(d: Int) = context = context :+ d
}

class Concrete_Test_Environment(override var context: Context = Seq.empty) extends Less_Abstract_Test_Environment

更好的方法是使用函数方法: context应该是val ,并且definion应该采用先前的值,然后返回新值:

    trait Abstract {
       type Context
       def initialContext: Context
       val context: Context = Range(1, 4)
         .foldLeft(initialContext) { case (c, n) => definition(c, n) }  
       def definition(context: Context, n: Int): Context

    }

    trait LessAbstract extends Abstract {
        override type Context = Seq[Int]
        override def definition(context: Context, n: Int) = context :+ n
    }

    class Concrete extends LessAbstract {
       override def initialContext = Seq(0)
    }

0
投票

您可以采用仅包含数据的白板的想法,该白板由许多仅包含逻辑(不包含数据!)的特征共享。 参见下面的一些未经测试的代码:

trait WhiteBoard {
  var counter: Int = 0
}

trait Display {
  var counter: Int
  def show: Unit = println(counter)
}

trait Increment {
  var counter: Int
  def inc: Unit = { counter = counter + 1 }
}

然后编写这样的单元测试:

val o = new Object with Whiteboard with Display with Increment
o.show
o.inc
o.show

这样,您就可以将数据的定义与需要数据的位置分开,这基本上意味着您可以按任何顺序混合特征。 唯一的要求是白板(定义数据)是混合的第一个特征。

© www.soinside.com 2019 - 2024. All rights reserved.