到目前為止,我們定義了關于布局元素類的一個層次結構。你可以把包含這個層次關系的類作為 API 接口提供給其它應用,但有時你可以希望對函數庫的用戶隱藏這種層次關系,這通??梢允褂?factory(構造工廠)對象來實現(xiàn)。一個 factory 對象定義了用來構造其它對象的函數。庫函數的用戶可以通過工廠對象來構造新對象,而不需要通過類的構造函數來創(chuàng)建類的實例。使用工廠對象的好處是,可以統(tǒng)一創(chuàng)建對象的接口并且隱藏被創(chuàng)建對象具體是如何來表示的。這種隱藏可以使得你創(chuàng)建的函數庫使用變得更簡單和易于理解,也正是隱藏部分實現(xiàn)細節(jié),可以使你有機會修改庫的實現(xiàn)而不至于影響庫的接口。
實現(xiàn) factory 對象的一個基本方法是采用 singleton 模式,在 Scala 中,可以使用類的伴隨對象(companion 對象)來實現(xiàn)。比如:
object Element {
def elem(contents: Array[String]):Element =
new ArrayElement(contents)
def elem(chr:Char, width:Int, height:Int) :Element =
new UniformElement(chr,width,height)
def elem(line:String) :Element =
new LineElement(line)
}
我們先把之前 Element 的實現(xiàn)列在這里:
abstract class Element {
def contents: Array[String]
def height: Int = contents.length
def width: Int = if (height == 0) 0 else contents(0).length
def above(that: Element) :Element =
new ArrayElement(this.contents ++ that.contents)
def beside(that: Element) :Element = {
new ArrayElement(
for(
(line1,line2) <- this.contents zip that.contents
) yield line1+line2
)
}
override def toString = contents mkString "\n"
}
有了 object Element(類 Element 的伴隨對象),我們可以利用 Element 對象提供的 factory 方法,重新實現(xiàn)類 Element 的一些方法:
abstract class Element {
def contents: Array[String]
def height: Int = contents.length
def width: Int = if (height == 0) 0 else contents(0).length
def above(that: Element) :Element =
Element.elem(this.contents ++ that.contents)
def beside(that: Element) :Element = {
Element.elem(
for(
(line1,line2) <- this.contents zip that.contents
) yield line1+line2
)
}
override def toString = contents mkString "\n"
}
這里我們重寫了 above 和 beside 方法,使用伴隨對象的 factory 方法 Element.elem 替代 new 構造函數。
這樣修改之后,庫函數的用戶不要了解 Element 的繼承關系,甚至不需要知道類 ArrayElement,LineElement 定義的存在,為了避免用戶直接使用 ArrayElement 或 LineElement 的構造函數來構造類的實例,因此我們可以把 ArrayElement,UniformElement 和 LineElement 定義為私有,定義私有可以也可以把它們定義在類 Element 內部(嵌套類)。下面為這種方法的使用:
object Element {
private class ArrayElement(val contents: Array[String])
extends Element {
}
private class LineElement(s:String) extends ArrayElement(Array(s)) {
override def width = s.length
override def height = 1
}
private class UniformElement (ch :Char,
override val width:Int,
override val height:Int
) extends Element{
private val line=ch.toString * width
def contents = Array.fill(height)(line)
}
def elem(contents: Array[String]):Element =
new ArrayElement(contents)
def elem(chr:Char, width:Int, height:Int) :Element =
new UniformElement(chr,width,height)
def elem(line:String) :Element =
new LineElement(line)
}