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

时间:2016-03-17 07:45:38

标签: scala module var init

我有一个架构问题,更准确地说,是一个次优的情况。

对于自适应测试环境,存在由一系列定义方法更新的上下文,每个定义方法定义不同的实体,即改变上下文。为简单起见,这里的定义只是整数,而上下文是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”中应用“definition”时,“Less_Abstract_Test_Environment”中的VAR“context”仍未绑定,即为null。在“Less_Abstract_Test_Environment”(来自“Abstract_Test_Environment”)中,def“定义”是“按需初始化”,而var“context”则不是。

我想出的“解决方案”是合并“Abstract_Test_Environment”和“Less_Abstract_Test_Environment”。这不是我想要的,因为它破坏了界面/目标和实现的自然分离,这已经由两个特征实现。

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

3 个答案:

答案 0 :(得分:1)

简单解决方案:除了处于底层课程外,不要在创建对象时初始化对象。相反,添加一个init方法,该方法包含所有初始化代码,然后在最底层的类中调用它(这是安全的,因为已经创建了所有父类)或者创建了对象。

整个事情的副作用是你甚至可以覆盖子类中的初始化代码。

答案 1 :(得分: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)
    }

答案 2 :(得分: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

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