Scala开发教程(41): 组合和继承–定义factory对象

jerry Scala 2015年11月25日 收藏

到目前为止,我们定义了关于布局元素类的一个层次结构。你可以把包含这个层次关系的类作为API接口提供给其它应用,但有时你可以希望对函数库的用户隐藏这种层次关系,这通常可以使用factory(构造工厂)对象来实现。一个factory对象定义了用来构造其它对象的函数。库函数的用户可以通过工厂对象来构造新对象,而不需要通过类的构造函数来创建类的实例。使用工厂对象的好处是,可以统一创建对象的接口并且隐藏被创建对象具体是如何来表示的。这种隐藏可以使得你创建的函数库使用变得更简单和易于理解,也正是隐藏部分实现细节,可以使你有机会修改库的实现而不至于影响库的接口。
实现factory对象的一个基本方法是采用singleton模式,在Scala中,可以使用类的伴随对象(companion 对象)来实现。比如:

  1. object Element {
  2. def elem(contents: Array[String]):Element =
  3. new ArrayElement(contents)
  4.  
  5. def elem(chr:Char, width:Int, height:Int) :Element =
  6. new UniformElement(chr,width,height)
  7.  
  8. def elem(line:String) :Element =
  9. new LineElement(line)
  10. }

我们先把之前Element的实现列在这里:

  1. abstract class Element {
  2. def contents: Array[String]
  3. def height: Int = contents.length
  4. def width: Int = if (height == 0) 0 else contents(0).length
  5. def above(that: Element) :Element =
  6. new ArrayElement(this.contents ++ that.contents)
  7. def beside(that: Element) :Element = {
  8. new ArrayElement(
  9. for(
  10. (line1,line2) <- this.contents zip that.contents
  11. ) yield line1+line2
  12. )
  13. }
  14. override def toString = contents mkString "\n"
  15.  
  16. }

有了object Element(类Element的伴随对象),我们可以利用Element对象提供的factory方法,重新实现类Element的一些方法:

  1. abstract class Element {
  2. def contents: Array[String]
  3. def height: Int = contents.length
  4. def width: Int = if (height == 0) 0 else contents(0).length
  5. def above(that: Element) :Element =
  6. Element.elem(this.contents ++ that.contents)
  7. def beside(that: Element) :Element = {
  8. Element.elem(
  9. for(
  10. (line1,line2) <- this.contents zip that.contents
  11. ) yield line1+line2
  12. )
  13. }
  14. override def toString = contents mkString "\n"
  15.  
  16. }

这里我们重写了above和beside方法,使用伴随对象的factory方法Element.elem替代new 构造函数。
这样修改之后,库函数的用户不要了解Element的继承关系,甚至不需要知道类ArrayElement,LineElement定义的存在,为了避免用户直接使用ArrayElement或LineElement的构造函数来构造类的实例,因此我们可以把ArrayElement,UniformElement和LineElement 定义为私有,定义私有可以也可以把它们定义在类Element内部(嵌套类)。下面为这种方法的使用:

  1. object Element {
  2.  
  3. private class ArrayElement(val contents: Array[String])
  4. extends Element {
  5. }
  6.  
  7. private class LineElement(s:String) extends ArrayElement(Array(s)) {
  8. override def width = s.length
  9. override def height = 1
  10. }
  11.  
  12. private class UniformElement (ch :Char,
  13. override val width:Int,
  14. override val height:Int
  15. ) extends Element{
  16. private val line=ch.toString * width
  17. def contents = Array.fill(height)(line)
  18. }
  19. def elem(contents: Array[String]):Element =
  20. new ArrayElement(contents)
  21.  
  22. def elem(chr:Char, width:Int, height:Int) :Element =
  23. new UniformElement(chr,width,height)
  24.  
  25. def elem(line:String) :Element =
  26. new LineElement(line)
  27. }
  28.