我們已經(jīng)看到 Trait 的一個主要用法,將一個瘦接口變成胖接口,本篇我們介紹 Trait 的另外一個重要用法,為類添加一些可以疊加的修改操作。Trait 能夠修改類的方法,并且能夠通過疊加這些操作(不同組合)修改類的方法。
我們來看這樣一個例子,修改一個整數(shù)隊列,這個隊列有兩個方法:put 為隊列添加一個元素,get 從隊列讀取一個元素。隊列是先進(jìn)先出,因此 get 讀取的順序和 put 的順序是一致的。
對于上面的隊列,我們定義如下三個 Trait 類型:
這三個 Trait 代表了修改操作,因為它們可以用來修改隊列類對象,而不是為隊列類定義所有可能的操作。這三個操作是可以疊加的,也就是說,你可以通過這三個基本操作的任意不同組合和原始的隊列類“混合”,從而可以得到你所需要的新的隊列類的修改操作。
為了實現(xiàn)這個整數(shù)隊列,我們可以定義這個整數(shù)隊列的一個基本實現(xiàn)如下:
import scala.collection.mutable.ArrayBuffer
abstract class IntQueue {
def get():Int
def put(x:Int)
}
class BasicIntQueue extends IntQueue{
private val buf =new ArrayBuffer[Int]
def get()= buf.remove(0)
def put(x:Int) { buf += x }
}
下面我們可以使用這個實現(xiàn),來完成隊列的一些基本操作:
scala> val queue = new BasicIntQueue
queue: BasicIntQueue = BasicIntQueue@60d134d3
scala> queue.put (10)
scala> queue.put(20)
scala> queue.get()
res2: Int = 10
scala> queue.get()
res3: Int = 20
這個實現(xiàn)完成了對象的基本操作,看起來了還可以,但如果此時有新的需求,希望在添加元素時,添加元素的雙倍,并且過濾掉負(fù)數(shù),你可以直接修改 put 方法 來完成,但之后需求又變了,添加元素時,添加的為參數(shù)的遞增值,你也可以修改 put 方法,這樣顯得隊列的實現(xiàn)不夠靈活。
我們來看看如果使用 Trait 會有什么結(jié)果,我們實現(xiàn) Doubling,Incrementing,F(xiàn)iltering 如下:
trait Doubling extends IntQueue{
abstract override def put(x:Int) { super.put(2*x)}
}
trait Incrementing extends IntQueue{
abstract override def put(x:Int) { super.put(x+1)}
}
trait Filtering extends IntQueue{
abstract override def put (x:Int){
if(x>=0) super.put(x)
}
}
我們可以看到所有的 Trait 實現(xiàn)都已 IntQueue 為基類,這保證這些 Trait 只能和同樣繼承了 IntQueue 的類“混合”,比如和 BasicIntQueue 混合,而不可以和比如前面定義的 Rational 類混合。
此外 Trait 的 put 方法中使用了 super,通常情況下對于普通的類這種調(diào)用是不合法的,但對于 trait來說,這種方法是可行的,這是因為 trait 中的 super 調(diào)用是動態(tài)綁定的,只要和這個 Trait 混合在其他類或 Trait 之后,而這個其它類或 Trait 定義了 super 調(diào)用的方法即可。這種方法是實現(xiàn)可以疊加的修改操作是必須的,并且注意使用 abstract override 修飾符,這種使用方法僅限于 Trait 而不能用作 Class 的定義上。
有了這三個 Trait 的定義,我們可以非常靈活的組合這些 Trait 來修改 BasicIntQueue 的操作。
首先我們使用 Doubling Trait
scala> val queue = new BasicIntQueue with Doubling
queue: BasicIntQueue with Doubling = $anon$1@3b004676
scala> queue.put(10)
scala> queue.get()
res1: Int = 20
這里通過 BasicIntQueue 和 Doubling 混合,我們構(gòu)成了一個新的隊列類型,每次添加的都是參數(shù)的倍增。
我們在使用 BasicIntQueue 同時和 Doubling 和 Increment 混合,注意我們構(gòu)造兩個不同的整數(shù)隊列,不同時 Doubling 和 Increment 的混合的順序
scala> val queue1 = new BasicIntQueue with Doubling with Incrementing
queue1: BasicIntQueue with Doubling with Incrementing = $anon$1@35849932
scala> val queue2 = new BasicIntQueue with Incrementing with Doubling
queue2: BasicIntQueue with Incrementing with Doubling = $anon$1@4a4cdea2
scala> queue1.put(10)
scala> queue1.get()
res4: Int = 22
scala> queue2.put(10)
scala> queue2.get()
res6: Int = 21
可以看到結(jié)果和 Trait 混合的順序有關(guān),簡單的說,越后混合的 Trait 作用越大。因此 queue1 先+1,然后 X2,而 queue 先 X2 后+1。
最后我們看看三個 Trait 混合的一個例子:
scala> val queue = new BasicIntQueue with Doubling with Incrementing with Filtering
queue: BasicIntQueue with Doubling with Incrementing with Filtering = $anon$1@73a4eb2d
scala> queue.put(10)
scala> queue.put(-4)
scala> queue.put(20)
scala> queue.get()
res10: Int = 22
scala> queue.get()
res11: Int = 42
scala> queue.get()
java.lang.IndexOutOfBoundsException: 0
at scala.collection.mutable.ResizableArray$class.apply(ResizableArray.scala:44)
at scala.collection.mutable.ArrayBuffer.apply(ArrayBuffer.scala:44)
at scala.collection.mutable.ArrayBuffer.remove(ArrayBuffer.scala:163)
at BasicIntQueue.get(<console>:11)
at .<init>(<console>:15)
at .<clinit>(<console>)
at .<init>(<console>:11)
at .<clinit>(<console>)
at $print(<console>)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:606)
at scala.tools.nsc.interpreter.IMain$ReadEvalPrint.call(IMain.scala:704)
at scala.tools.nsc.interpreter.IMain$Request$$anonfun$14.apply(IMain.scala:920)
at scala.tools.nsc.interpreter.Line$$anonfun$1.apply$mcV$sp(Line.scala:43)
at scala.tools.nsc.io.package$$anon$2.run(package.scala:25)
at java.lang.Thread.run(Thread.java:744)
最后的異常時因為隊列為空(過濾掉了負(fù)數(shù)),我們沒有添加錯誤處理,元素 -4 沒有被添加到了隊列中。
由此可以看出,通過 Trait 可以提高類的實現(xiàn)的靈活性,你可以通過這些 Trait 的不同組合定義了多種不同的對列類型。
更多建議: