【问题标题】:Scala: Inheritance of static methods vs. DRY-principle vs. encapsulationScala:静态方法的继承与 DRY 原理与封装
【发布时间】:2015-07-09 19:40:14
【问题描述】:

我正在尝试在 Scala 中实现 OOP 范式。我将拥有一个包含 50-100 个子类的抽象基类。这些子类中的每一个都应该能够生成一些用于测试目的的随机实例。 (事实上​​,我的真实生活场景比这要复杂得多,但我认为这足以解决这个问题。)

无论我怎么做,我对解决方案都不满意。我希望你们中的一些 Scala 专家可以帮助我以 Scala 的方式思考这个问题。

如果在 Scala 中允许使用静态,我会这样做:

abstract class Base {
  protected val instanceValues: List[SomeType] // i.e. row-number, full-URL etc.
  def toString():String = "Base[" + classValues.toString() + "]: " + instanceValues.toString()
  protected static def classValues: SomeOtherType // i.e. table-name, domain-name etc.
  static def genData(): List[Base] = /* some default implementation using only classValues */
}

class A(override val instanceValues: List[SomeType]) extends Base {
  protected static def classValues = new SomeType(/* A-specific */)
}

...

class Z(override val instanceValues: List[SomeType]) extends Base {
  protected static def classValues = new SomeType(/* Z-specific */)
}

class SpecialCase(override val instanceValues: List[SomeType]) extends Base {
  protected protected def classValues = new SomeType(/* SpecialCase-specific */)
  override static def genData(): List[Base] = /* something specific to this subclass not easily expressed elegantly using classValues */
}

但是,由于 Scala 中不允许使用静态,因此从来都不是真正的解决方案。

阅读诸如this 之类的内容(注意:这个问题不是那个问题的重复 - 这涉及到我认为的不优雅 - 使用伴随对象解决方案)它看起来我需要创建 28相同的伴随对象来容纳 classValues- 和 genData-methods:

abstract class Base {
  protected val instanceValues: List[SomeType]
}

class A(override val instanceValues: List[SomeType]) extends Base {
  def toString():String = "Base[" + A.classValues.toString() + "]: " + instanceValues.toString()
}
object A {
  private val classValues: SomeOtherType
  static def genData(): List[Base] = /* some default implementation using only classValues */
}

...

class Z(override val instanceValues: List[SomeType]) extends Base {
  def toString():String = "Base[" + Z.classValues.toString() + "]: " + instanceValues.toString()
}
object Z {
  private val classValues: SomeOtherType
  static def genData(): List[Base] = /* some default implementation using only classValues */
}

class SpecialCase(override val instanceValues: List[SomeType]) extends Base {
  def toString():String = "Base[" + SpecialCase.classValues.toString() + "]: " + instanceValues.toString()
}
object SpecialCase {
  private val classValues: SomeOtherType
  static def genData(): List[Base] = /* something specific for SpecialCase */
}

除了有很多膨胀之外,这个解决方案似乎违反了 DRY 原则,它也迫使我以几乎相同的方式重新实现共享的 toString 方法。最后,这意味着任何扩展 Base 的人都应该记住为新类添加一个伴生对象。

另一种解决方案,是拥有一个“测试数据”-工厂:

abstract class Base {
  val instanceValues: List[SomeType]
  def classValues: SomeOtherType
  def toString():String = "Base[" + classValues.toString() + "]: " + instanceValues.toString()
}

object TestDataGenerator {
  def genData(clss:String): List[Base] = clss match {
    case "SpecialCase" => /* something specific to SpecialCase */
    case other => /* some default implementation using reflection for creation and some kind of manipulation of the SomeType and SomeOtherType objects after creation */
  }
}

class A(override val instanceValues: List[SomeType]) extends Base {
  def classValues = new SomeType(/* A-specific */)
}

...

class Z(override val instanceValues: List[SomeType]) extends Base {
  def classValues = new SomeType(/* Z-specific */)
}

class SpecialCase(override val instanceValues: List[SomeType]) extends Base {
  def classValues = new SomeType(/* SpecialCase-specific */)
}

但这需要我打开对字段 instanceValues 和 classValues 的读取访问权限,这是不可取的。

【问题讨论】:

  • 这可以通过使用代数数据类型轻松解决。谷歌这个词。当我有更多时间时,我会发布一个解决方案,如果在此期间没有其他人的话。
  • 我不认为这个问题与那个问题重复。那个一般涉及如何在Scala中做“静态”。这处理了在我的用例中使用伴随对象解决方案的不优雅——正如我所看到的那样。
  • @I.K.我已经阅读了代数数据类型,虽然它们很有趣,但我看不出它们在这里有什么帮助,所以我当然希望你能有更多时间在某个时候发帖。

标签: scala oop


【解决方案1】:

为了后代:我最终将架构更改为由特征和对象组成,并使用“覆盖”内部类来创建实例。这不是很多美丽,但至少我避免了问题中列出的缺陷。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2023-04-05
    • 2010-11-20
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多