Scala:静态方法与DRY原则与封装的继承

时间:2015-07-01 11:41:23

标签: scala oop

我正在尝试在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的人都应该记得为新类添加一个伴随对象。

另一种解决方案是使用"测试数据" -factory:

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字段的读访问权,这是不可取的。

1 个答案:

答案 0 :(得分:0)

对于后人:我最终改变了我的架构,包括特征和对象,"覆盖"用于创建实例的内部类。这不是很多美,但至少我避免了问题中列出的缺陷。