學習Scala的定義工廠對象
你現在有了布局元素的類層級。這個層級可以“依原件”展現給你的客戶。但是你或許還是選擇把層級隱藏在工廠對象之后。工廠對象包含了構建其它對象的方法。客戶與實惠使用這些工廠方法實現對象的構造而不是直接使用new構造對象。這種方式的一個好處是對象的創建可以被集中化并且對象實際代表類的細節可以被隱藏。這種隱藏一方面簡化客戶理解你的庫,因為更少的細節被暴露出來,另一方面提供給你更多機會在之后改變庫的實現而不會破壞客戶代碼。
51CTO編輯推薦:Scala編程語言專題
為布局元素構建工廠的第一任務是選擇工廠方法應該放在哪兒。它們應該是單例對象成員還是類成員?包含它們的對象或類應該怎么調用?這里有許多可能性。最直接的方案是創建類Element的伴生對象并把它做成布局元素的工廠方法。對于這種方式,你唯一要暴露給客戶的就是Element的類/對象組合,隱藏它的三個實現類ArrayElement,LineElement和UniformElement。
代碼10.10是遵循了這個方案的設計。Element伴生對象包含了三個重載的elem方法變體。每一個變體構建一種不同的布局對象。
代碼 10.10 帶有工廠方法的工廠對象
- 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的實現通過使用elem工廠方法實現而不用顯式地創建新的ArrayElement實例成為可能。為了不使用單例對象的名稱,Element,認證而調用工廠方法,我們將在源文件頂上引用Element.elem。換句話說,代之以在Element類內部使用Element.elem調用工廠方法,我們將引用Element.elem,這樣我們只要使用它們的簡化名,elem,就可以調用工廠方法。代碼10.11展示了類Element在這些改變之后的樣子。
代碼 10.11 重構以使用工廠方法的類Element
- import Element.elem
- abstract class Element {
- def contents: Array[String]
- def width: Int =
- if (height == 0) 0 else contents(0).length
- def height: Int = contents.length
- def above(that: Element): Element =
- elem(this.contents ++ that.contents)
- def beside(that: Element): Element =
- elem(
- for (
- (line1, line2) < - this.contents zip that.contents
- ) yield line1 + line2
- )
- override def toString = contents mkString "\n"
- }
而且,有了工廠方法之后,子類ArrayElement,LineElement和UniformElement現在可以是私有的,因為它們不再需要直接被客戶訪問。Scala里,你可以在類和單例對象中定義其它的類和單例對象。因此一種讓Element的子類私有化的方式就是把它們放在Element單例對象中并在那里聲明它們為私有。需要的時候,這些類將仍然能被三個elem工廠方法訪問。代碼10.12展示了其中的細節。
- object Element {
- private class ArrayElement(
- val contents: Array[String]
- ) extends Element
- private class LineElement(s: String) extends Element {
- val contents = 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.make(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)
- }
代碼 10.12 用私有類隱藏實現
【相關閱讀】