0

在 Scala 中有一种方法可以在 main 中创建所有实例。

例如,我有以下类:作者、页面、书籍。我主要创建这三个类的实例。在 Book 的一个实例中,我放置了作者和页面。

然后我想检查是否有不属于任何书的页面。

这个例子只是解释性的,你不要太重视它们。

谢谢你们。抱歉英文翻译不好。

4

4 回答 4

3

简短的回答:不,没有完全自动化的方法可以做到这一点。您需要遍历所有分配的对象,这是垃圾收集器可以做的事情,但程序本身不能。

以下是跟踪哪些子对象已附加到根对象的一种可能方法的草图。

定义允许我们区分子对象和根对象的基本特征:

trait Root

trait Sub {
  def register(r: Root)
  def deregister(r: Root)
  def isRegistered: Boolean
}

Book然后我们定义一个可以添加页面的根对象:

class Book(val title: String) extends Root {
  private var pages: Set[Page] = Set()

  Books.books += this

  def add(p: Page) {
    pages += p
    p.register(this)
  }

  def remove(p: Page) {
    pages -= p
    p.deregister(this)
  }
}

在实例化时,每本书都将自己添加到一个全局存储库中:

object Books {
  var books: Set[Book] = Set()
}

同样对于子对象Page

class Page(number: Int) extends Sub {
  private var owner: Option[Root] = None

  Pages.pages += this

  def register(r: Root) = owner match {
    case None =>
      owner = Some(r)
    case Some(o) =>
      sys.error("%s is already owned by %s, thus it cannot be owned by %s"
                .format(this, o, r))
  }

  def deregister(r: Root) = owner match {
    case Some(s) if r == s =>
      owner = None
    case Some(s) =>
      sys.error("%s is owned by %s, but not by %s"
                .format(this, s, r))
    case None =>
      sys.error("%s is not owned at all, thus also not by %s"
                .format(this, r))
  }

  def isRegistered = owner.nonEmpty
}

object Pages {
  var pages: Set[Page] = Set()

  def allRegistered = pages.forall(_.isRegistered)
}

一些使用示例:

val p1 = new Page(1)
val p2 = new Page(2)
val p3 = new Page(3)

val b1 = new Book("Fahrenheit 451")
val b2 = new Book("Brave New World")

b1.add(p1)
b1.add(p2)
b2.add(p3)

println(Pages.allRegistered) // true

val p4 = new Page(4)

println(Pages.allRegistered) // false

b2.add(p4)
b2.remove(p3)

println(Pages.allRegistered) // false

b2.add(p1) // Exception: already owned


注意:要记住的设计决策,开放式问题:

  • 如果要销毁子对象,如何进行?从全局存储库中删除它?

  • 一个子对象可以被多个根对象拥有吗?

  • 拥有多个非全局存储库会更好吗?这可能意味着我们需要工厂来创建和注册子对象。

  • 在并发设置中,我们必须同步对(全局)存储库的访问

  • 我们可以将用于(取消)注册子对象的代码移动到单个特征中,然后在每个根对象中重用它吗?


注二:如果我们有反向指针,我们只需要一个(全局)子对象存储库。但是,我不知道任何提供内置反向指针的语言。

于 2012-11-15T13:32:05.900 回答
1

不,您必须自己跟踪它们。不要认为任何语言真的可以做到这一点。当然,你可以编写你的类,让它们这样做,但这是你的责任。

于 2012-11-15T13:12:00.543 回答
1

你可以

// If you use the REPL, :paste both class and object together
class Page {
  Page.everything += this
  var booked: Option[Book] = None   // Or just set to null
}
object Page {
  val everything: collection.mutable.HashSet[Page] = collection.mutable.HashSet()
}

class Book {
  var pages: Vector[Page] = Vector()
  def addPage(p: Page) { p.booked = Some(this); pages = pages :+ p }
}

// Create all your books and pages and assign them all

Page.everything.filter(_.booked.isEmpty)   // This contains all unassigned pages

跟踪自己。(这是假设单线程代码;以您的专业水平,可能不值得担心如何并行执行此操作。)

或者,如果您想确保main方法中的所有事情都发生,您可以执行类似的操作

// File Page.scala
class Page(text: String)(implicit mhm: MustHaveMe) {
}

// File Main.scala
object Main {
  sealed trait MustHaveMe
  private final object YesYouHaveMe extends MustHaveMe
  def main(args: Array[String]) {
    implicit val okayHere = YesYouHaveMe
    new Page("Once upon a time")
  }
}

现在,访问限制将阻止任何人Main.scala创建新页面,并且只有文件中的main方法Main.scala具有创建Page. 因此,您可以更轻松地避免在其他地方创建对象。

于 2012-11-15T13:30:32.363 回答
0

我必须能够独立创建我的页面和书籍 我感兴趣的事情是跟踪创建的所有实例。正如你所建议的,我想我也部署了我的课本:

class Book(var name: String, var pages: List[Page]){
AllBook.allbook += this
}

object AllBook {
var allbook: Set[Book] = Set()
}

以类似的方式还有 Page 类。作为 main 方法中的最后一条指令,使用必须控制我的约束的方法调用“验证”。

这可能没问题?有更好的解决方案吗?

注意:约束是独立的,尊重类的实现

请原谅我的英文翻译不好

于 2012-11-16T16:05:51.757 回答