精品欧美一区二区三区在线观看 _久久久久国色av免费观看性色_国产精品久久在线观看_亚洲第一综合网站_91精品又粗又猛又爽_小泽玛利亚一区二区免费_91亚洲精品国偷拍自产在线观看 _久久精品视频在线播放_美女精品久久久_欧美日韩国产成人在线

Scala編程指南 了解Traits功能

開發(fā) 后端
本文為《Scala編程指南》的第四部分,將介紹Traits功能。Scala 是一種基于JVM,集合了面向對象編程和函數式編程優(yōu)點的高級程序設計語言。

近日,Scala的創(chuàng)始人發(fā)表了“Scala is for good programmers(Scala語言是給專家級程序員的)”這樣的言論!Scala在現在這個階段并不需要適合一般的Java程序員。要吸引的是一些專家級的程序員——優(yōu)秀的程序員。目標是使他們工作起來比使用Java更有效率。只有會出現足夠多的教育示范材料和足夠好的開發(fā)工具時,Scala對廣大的普通開發(fā)人員才具有吸引力。但是Scala語言什么呢?

Scala編程指南》系列文章將會詳細介紹Scala語言。本文為《Scala編程指南》系列的第四章,將介紹Traits功能。

Traits 介紹

在我們深入面向對象編程之前,我們還需要了解Scala 一個特性:Traits。要了解這個功能需要一點歷史知識。

在Java 中,一個類可以實現任意數量的接口。這個模型在聲明一個類實現多個抽象的時候非常有用。不幸的是,它也有一個主要缺點。

對于許多接口,大多數功能都可以用對于所有使用這個接口的類都有效的“樣板”代碼來實現。Java 沒有提供一個內置機制來定義和使用這些可重用代碼。相反的,Java 程序員必須使用一個特別的轉換來重用一個已知接口的實現。在最壞的情況下,程序員必須復制粘貼同樣的代碼到不同的類中去。

通常,一個接口的實現擁有和該實例的其它成員無關(正交)的成員。術語mixin (混合)通常被用來指實例中這些專注的,潛在可被重用的,并且可以獨立維護的代碼。

來看一下下面這個圖形用戶接口的按鈕的代碼,它對單擊事件使用了回調。

  1. // code-examples/Traits/ui/button-callbacks.scala  
  2. package ui  
  3. class ButtonWithCallbacks(val label: String,  
  4.     val clickedCallbacks: List[() => Unit]) extends Widget {  
  5.  
  6.   require(clickedCallbacks != null, "Callback list can't be null!")  
  7.  
  8.   def this(label: String, clickedCallback: () => Unit) =  
  9.     this(label, List(clickedCallback))  
  10.  
  11.   def this(label: String) = {  
  12.     this(label, Nil)  
  13.     println("Warning: button has no click callbacks!")  
  14.   }  
  15.  
  16.   def click() = {  
  17.     // ... logic to give the appearance of clicking a physical button ...  
  18.     clickedCallbacks.foreach(f => f())  
  19.   }  
  20. }  

這里發(fā)生了很多事情。主構造函數接受一個label(標簽)參數和一個callbacks(回調)的list(列表),這些回調函數會在按鈕的click 方法被調用時被調用。我們會在《第5章 - Scala 基礎面向對象編程》中來探索這個類的更多細節(jié)。現在,我們希望專注在一個特別的問題上。ButtonWithCallbacks 不僅處理按鈕的一些本質行為(比如單擊),它同時還通過調用回調函數處理單擊事件的通知。這違反了單職原則[Martin2003],這是分隔職能的一種設計方法。我們可以把按鈕類的邏輯從回調邏輯中分離出來,這樣每一個邏輯組件變得更加簡單,更模塊化,更可重用化。這個回調邏輯就是mixin 的一個不錯例子。

這樣的分離在Java 中很難做,即使我們定義了具有回調行為的接口,我們仍然需要在類中集成實現代碼,降低模塊性。唯一的其它方法則是使用特定的工具比如面向方面編程(Aspect-Oriented Programming,AOP,參見[AOSD]),一個實現是AspectJ,Java 的一個擴展。AOP 主要被設計用來分離在應用程序中重復出現的普遍問題的實現。它設法模塊化這些關鍵點,但是也允許設計良好的和其它關鍵點行為的“混合”,包括應用程序的核心域邏輯,不管是在編譯時還是運行時。

作為混合體的Traits

Scala 提供了完整的混合(mixin)解決方案,稱為Traits。在我們的例子里,我們可以定義回調的抽象為一個Trait,就像一個Java 接口一樣,但是我們可以實現這些Trait (或者繼承的Trait)的抽象。我們可以定義混合了Trait 的類,大致上很像實現Java 的一個接口。不過,在Scala 你甚至可以在我們創(chuàng)建實例的時候混合Traits。也就是說,我們不必首先聲明一個混合了所有我們所需要的Trait 的類。所以,Scala Traits 在保留分離關鍵點的同時給了我們按需整合行為的能力。

如果你來自于Java 編程世界,你可以認為Traits 是有選擇地實現了的接口。其它語言提供了類似Trait 的結構,比如Ruby 中的模塊(modules)。

讓我們對按鈕的邏輯來使用Trait,從而分離回調的處理。我們會推廣一下我們的實現。回調其實是觀察者模式[GOF1995] 的一個特例。所以,讓我們創(chuàng)建一個Trait 來實現這個模式,然后用它來處理回調行為。為了簡單化,我們從一個單獨的計算按鈕被按次數的回調開始。

首先,讓我們定義一個簡單的Button 類。

  1. // code-examples/Traits/ui/button.scala  
  2. package ui  
  3. class Button(val label: String) extends Widget {  
  4.   def click() = {  
  5.     // Logic to give the appearance of clicking a button...  
  6.   }  
  7. }  
  8.  

這里是它的父類,Widget。

  1. // code-examples/Traits/ui/widget.scala  
  2. package ui  
  3. abstract class Widget  
  4.  

管理回調的邏輯(比如,clickedCallbacks 列表)被省略了,兩個主要構造函數也是。只有按鈕的label 字段和click 方法被保留了下來。這個click 方法現在只關心一個“物理上的” 按鈕被單擊時候的可見表現。按鈕只有一個關心的東西,就是處理作為一個按鈕的本質行為。

這里是一個實現了觀察者模式邏輯的Trait。

  1. // code-examples/Traits/observer/observer.scala  
  2. package observer  
  3. Trait Subject {  
  4.   type Observer = { def receiveUpdate(subject: Any) }  
  5.   private var observers = List[Observer]()  
  6.   def addObserver(observer:Observer) = observers ::observer 
  7.   def notifyObservers = observers foreach (_.receiveUpdate(this))  
  8. }  
  9.  

除了Trait 關鍵字,Subject 看起來就像一個普通的類。Subject 定義了所有它聲明的成員。Traits 可以聲明抽象成員,具體成員,或者兩者皆有,就像類所能做的一樣(參見《第6章 - Scala 高級面向對象編程》的“重寫類和Traits 的成員”章節(jié)獲取更多信息)。而且,Traits 能包含嵌套的Trait 和類定義,類也能包含嵌套的Trait 定義。

第一行定義了Observer 類型。這是一個結構類型,形式是 { def receiveUpdate(subject:Any) }。結構類型僅制定了子類型必須支持的結構;你可以把它們看作是“匿名”類型。

在這個例子里,這個結構類型由一個有著特定簽名的方法定義。任何有這個簽名的方法的類型都可以被用作為一個observer(觀察者)。我們會在《第12章 - Scala 類型系統》學習更多有關結構類型的內容。如果你想知道為什么我們不把Subject 作為參數,而是Any。我們會在《第13章 - 應用程序設計》的“自我類型注解和抽象類型成員”章節(jié)來重習這個問題。

我們所需要注意的最主要的是這樣的結構類型如何最小化了Subject Trait 和任何潛在的Trait 用戶之間的耦合。

注意

Subject 仍然通過結構類型和Observer 中的方法名稱耦合在一起,例如,名為receiveUpdate 的方法。我們有幾種方法來省去這剩下的耦合。我們會在《第6章 - Scala 高級面向對象編程》中的“重寫抽象類型”章節(jié)看到如何做到這一點。

下面,我們聲明了一系列觀察者。我們定義了一個var,而不是val,因為List 是不可變的。所以我們必須在一個觀察者通過addObserver 方法被添加時創(chuàng)建一個新的列表。

我們會在《第7章 - Scala 對象系統》的“Scala 類型結構”章節(jié)和《第8章 - Scala 函數式編程》中討論更多有關List 的細節(jié)。現在,注意addObserver 使用了列表的cons “操作符”方法(::)來在一個列表前面加入一個觀察者。Scala 編譯器會聰明地把下面的語句,

  1. observers ::observer 
  2.  

轉換成如下語句,

  1. observerobservers = observer :: observers  
  2.  

注意我們寫了observer:: observers,把已存的observers 列表放到了右邊。回憶一下,所有的以: 結尾的方法是右綁定的。所以,前一個語句和下面的語句等價。

  1. observersobservers = observers.::(observer)  
  2.  

notifyObservers 方法遍歷所有的觀察者,使用foreach 方法,然后對每一個觀察者調用receiveUpdate 方法。(注意我們使用了“插入”操作符標記法而不是observers.foreach。)我們使用占位符'_' 來縮短下面的表達式,

  1. (obs) => obs.receiveUpdate(this)  
  2.  

為這樣的表達式,

  1. _.receiveUpdate(this)  
  2.  

這個表達式實際上是一個“匿名函數”的函數體,在Scala 中稱為字面函數。這和其它語言中的Lambda 表達式或類似結構相似。字面函數和閉包相關的概念會在《第8章 - Scala 函數式編程》的“字面函數和閉包”章節(jié)中被討論。

在Java 中,foreach 方法很可能會接受一個接口,你可能會傳遞一個實現了該接口的類的實例。(比如,典型的Comparable 使用的方法)。

在Scala 中,List[A].foreach 方法期待的參數類型為(A)=>Unit,這是一個函數,接受一個A 類型的參數,而A 標識了列表的元素的類型(在這個例子中是Observer),然后返回Unit(和Java 的void 一樣)。

注意

我們在這個例子里選擇使用一個var 來表示不可變的觀察者的List。我們也可以使用val 和一個可變的類型,比如ListBuffer。這個選擇會在一個真實的應用程序中顯得更加合理,但是我們希望避免介紹新的類來分散我們的注意。

再一次的,我們從一個小例子里學習了許多Scala 的知識。現在,讓我們來用一用我們的Subject Trait。這里有一個ObservableButton,它繼承了Button,混合了Subject。

  1. // code-examples/Traits/ui/observable-button.scala  
  2. package ui  
  3. import observer._  
  4. class ObservableButton(name: String) extends Button(name) with Subject {  
  5.   override def click() = {  
  6.     super.click()  
  7.     notifyObservers  
  8.   }  
  9. }  
  10.  

我們從導入observer 包的所有東西開始,使用'_' 通配符。實際上,我們在這個包中只定義了Subject Trait。

新的類使用了with 關鍵字把Subject Trait 加到類中。ObserverButton 重寫了click 方法。使用super 關鍵字(參見《第6章 - Scala 高級面向對象編程》的“重寫抽象和具體方法”章節(jié)),它首先調用了“父類”的方法,Button.click,然后它通知觀察者。因為新的click 方法重寫了Button 的具體實現,必須加上override 關鍵字。

with 關鍵字和Java 的對接口使用的implement 關鍵字類似。你可以是頂任意多的Traits,每一個都必須有with 關鍵字。

一個類可以繼承一個Trait,一個Trait 也可以繼承一個類。實際上,我們的Widget 類也可以被聲明為一個Trait。

注意

如果你定義一個類使用一個或多個Traits,而它又不繼承任何類,你必須對第一個列出的Trait 使用extends 關鍵字。

如果你不對第一個Trait 使用extends,例如寫成這樣。

  1. // ERROR:  
  2. class ObservableButton(name: String) with Button(name) with Subject {...}  
  3.  

你會獲得如下錯誤。

  1. ... error: ';' expected but 'with' found.  
  2.        class ObservableButton(name: String) with Button(name) with Subject {...}  
  3.                                             ^  
  4.  

這個錯誤實際上應該說“with found,but extends expected。”(發(fā)現with 關鍵字,但是期望一個extends。)

要演示這部分代碼,讓我們從一個觀察按鈕點擊和記錄點擊數目的類開始。

  1. // code-examples/Traits/ui/button-count-observer.scala  
  2. package ui  
  3. import observer._  
  4. class ButtonCountObserver {  
  5.   var count = 0 
  6.   def receiveUpdate(subject: Any) = count += 1  
  7. }  
  8.  

左后,讓我們寫一個測試來運用所有的類。我們會使用Specs 庫(在《第14章 - Scala 工具,庫和IDE 支持》的“Specs” 章節(jié)討論) 來寫一個行為驅動(【BDD】)的“規(guī)范”來測試組合后的Button 和Subject 類型。

  1. // code-examples/Traits/ui/button-observer-spec.scala  
  2. package ui  
  3. import org.specs._  
  4. import observer._  
  5. object ButtonObserverSpec extends Specification {  
  6.   "A Button Observer" should {  
  7.     "observe button clicks" in {  
  8.       val observableButton = new ObservableButton("Okay")  
  9.       val buttonObserver = new ButtonCountObserver  
  10.       observableButton.addObserver(buttonObserver)  
  11.       for (i <- 1 to 3) observableButton.click()  
  12.       buttonObserver.count mustEqual 3  
  13.     }  
  14.   }  
  15. }  
  16.  

如果你從O'Reilly 網站下載了代碼例子,你可以按照README 文件的指示來編譯和運行這個章節(jié)的例子。specs “目標”的輸出應該包含如下的內容。

  1. Specification "ButtonCountObserverSpec"  
  2.   A Button Observer should  
  3.   + observe button clicks  
  4. Total for specification "ButtonCountObserverSpec":  
  5. Finished in 0 second, 10 ms  
  6. 1 example, 1 expectation, 0 failure, 0 error  
  7.  

注意字符串“A Button Observer Should”和“observe button clicks” 對應了例子中的字符串。Specs 的輸出提供了一個漂亮的被測試的項目的需求,并假設為這些字符串做了合適的決定。

測試的主題創(chuàng)建了一個“Okay” ObservableButton 和一個ButtonCountObserver,把觀察者給了這個button。按鈕通過for 循環(huán)被按了3次。最后一行要求observer 的計數等于3。如果你習慣使用XUnit 風格的TDD (測試驅動開發(fā))工具,例如JUnit 或者ScalaTest(參見《第14章 - Scala 工具,庫和IDE 支持》的“ScalaTest” 章節(jié)),那么最后一行等效于下面的JUnit 斷言。

  1. assertEquals(3, buttonObserver.count)  
  2.  

注意

Specs 庫(參見“Specs” 章節(jié)) 和ScalaTest 庫(參見“ScalaTest”章節(jié))都支持行為驅動開發(fā)[BDD],測試驅動開發(fā)[TDD] 的一種風格,強調了測試的“規(guī)范”角色。

假設我們只需要一個ObservableButton 實例呢?我們實際上不用聲明一個繼承Subject 的Button 的子類。我們可以在創(chuàng)建實例的時候加上Trait。

下一個例子展示了一個修訂的Specs 文件,它實例化了一個Button,混合了Subject 作為聲明的一部分。

  1. // code-examples/Traits/ui/button-observer-anon-spec.scala  
  2. package ui  
  3. import org.specs._  
  4. import observer._  
  5. object ButtonObserverAnonSpec extends Specification {  
  6.   "A Button Observer" should {  
  7.     "observe button clicks" in {  
  8.       val observableButton = new Button("Okay") with Subject {  
  9.         override def click() = {  
  10.           super.click()  
  11.           notifyObservers  
  12.         }  
  13.       }  
  14.       val buttonObserver = new ButtonCountObserver  
  15.       observableButton.addObserver(buttonObserver)  
  16.       for (i <- 1 to 3) observableButton.click()  
  17.       buttonObserver.count mustEqual 3  
  18.     }  
  19.   }  
  20. }  
  21.  

修訂過的observableButton 的聲明實際上創(chuàng)建了一個匿名類,并且像之前那樣重寫了click 方法。和在Java 中創(chuàng)建匿名類的主要區(qū)別是我們可以在過程中引入Trait。Java 不允許你在實例化一個類的時候實現一個新的接口。

最后,注意一個實例的繼承結構會因為混合了繼承自其它Traits 的Traits 而變得復雜。我們會在《第7章 - Scala 對象系統》的“對象層次結構的線性化”章節(jié)來討論這些層次結構的細節(jié)。

#p#

可堆疊Traits

我們可以通過一系列精煉來提高我們工作的可重用性,使得我們可以更容易地同時使用一個以上的Trait,例如,“堆疊”它們。

首先,讓我們來引入一個新的Trait,Clickable,一個任意構件響應點擊事件的抽象。

  1. // code-examples/Traits/ui2/clickable.scala  
  2. package ui2  
  3. Trait Clickable {  
  4.   def click()  
  5. }  
  6.  

注意

我們由一個新的包,ui2 開始,這樣我們可以更容易的區(qū)分開下載下來的新舊代碼。

Clickable Trait 看上去就像一個Java 接口;它是完全抽象的。它定義了一個單獨的抽象的方法,click。因為它沒有函數體,所以稱之為抽象。如果Clickable 是一個類的話,我們則應該在class 關鍵字前面加上abstract 關鍵字。但是這對于Trait 來說不是必須的。

這里是重構過的按鈕類,使用了這個Trait。

  1. // code-examples/Traits/ui2/button.scala  
  2. package ui2  
  3. import ui.Widget  
  4. class Button(val label: String) extends Widget with Clickable {  
  5.   def click() = {  
  6.     // Logic to give the appearance of clicking a button...  
  7.   }  
  8. }  
  9.  

這段代碼就像Java 實現一個Clickable 接口一樣。

當我們在前面定義ObservableButton 的時候(在“混合Traits”章節(jié)),我們重寫了Button.click 來通知觀察者。而我們在聲明observableButton 為一個按鈕實例的時候,我們直接混合了Subject Trait,它重復了ButtonObserverAnonSpec 的邏輯。讓我們來消除這個重復。

當我們開始用這種方式重構代碼的時候,我們意識到我們實際上不關心“觀察”按鈕;我們關心的是“觀察”點擊。這里是一個單一的專注于觀察點擊的Trait。

  1. // code-examples/Traits/ui2/observable-clicks.scala  
  2. package ui2  
  3. import observer._  
  4. Trait ObservableClicks extends Clickable with Subject {  
  5.   abstract override def click() = {  
  6.     super.click()  
  7.     notifyObservers  
  8.   }  
  9. }  
  10.  

ObservableClick Trait 繼承自Clickable,并且混合了Subject。然后它重寫了click 方法,像在“混合Traits”章節(jié)重寫的方法幾乎一樣的實現。最重要的區(qū)別就是abstract 關鍵字。

仔細看這個方法。它調用了super.click(),但是這里super 是什么意思?在這里,它看上去只能是聲明了但是沒有定義click 方法的Clickable,或者Subject,它并沒有click 方法。所以,super 的身份還不一定,至少現在還不一定。

實際上,super 會在這個Trait 混入一個定義了具體click 方法的實例的時候被綁定。這樣,我們需要在ObservableClicks.click 前加上abstract 關鍵字來告訴編譯器(或者讀者)click 還沒有被完全實現,即使ObservableClicks.click 有一個函數體。

注意

除了聲明抽象類,abstract 關鍵字只在Trait 的方法有函數體,但是調用了在父類沒有具體實現的super 的方法的時候需要。

讓我們在Specs 測試中和Button 類以及它的具體click 方法一起使用這個Trait。

  1. // code-examples/Traits/ui2/button-clickable-observer-spec.scala  
  2. package ui2  
  3. import org.specs._  
  4. import observer._  
  5. import ui.ButtonCountObserver  
  6. object ButtonClickableObserverSpec extends Specification {  
  7.   "A Button Observer" should {  
  8.     "observe button clicks" in {  
  9.       val observableButton = new Button("Okay") with ObservableClicks  
  10.       val buttonClickCountObserver = new ButtonCountObserver  
  11.       observableButton.addObserver(buttonClickCountObserver)  
  12.       for (i <- 1 to 3) observableButton.click()  
  13.       buttonClickCountObserver.count mustEqual 3  
  14.     }  
  15.   }  
  16. }  
  17.  

把這段代碼和ButtonObserverAnonSpec 比較。我們初始化了一個Button,混入ObservableClicks Trait,但是這次我們不需要重寫click 方法。所以,這個Button 的使用者不用惦記著重寫一個合適的click。這部分工作已經由ObservableClicks 完成。想要的行為在我們需要的時候被聲明性地組合到代碼里去。

讓我們再來加入一個Trait。JavaBeans 規(guī)范[JavaBeanSpec] 有“可否決”事件的概念,是說JavaBean 修改的監(jiān)聽者可以否決修改。讓我們來用Trait 來實現一個類似的機制,用來否決一系列的點擊。

  1. // code-examples/Traits/ui2/vetoable-clicks.scala  
  2. package ui2  
  3. import observer._  
  4. Trait VetoableClicks extends Clickable {  
  5.   val maxAllowed = 1  // default  
  6.   private var count = 0 
  7.   abstract override def click() = {  
  8.     if (count < maxAllowed) {  
  9.       count += 1  
  10.       super.click()  
  11.     }  
  12.   }  
  13. }  
  14.  
  15.     
  16.  

再一次,我們重寫了click 方法。和以前一樣,必須聲明這個重寫是抽象的。允許點擊的數目默認值是1。你可能想知道這里的默認是什么?這個字段不是被聲明為val 嗎? 我們沒有聲明構造函數用其它值來初始化它。我們會在《第6章 - Scala 高級面向對象編程》的”重寫類和Traits 的成員“ 重溫這個問題。

這個Trait 還聲明了一個count 變量來記錄我們觀察到的點擊。它被定義為private (私有的),所以它對于Trait 外部的域來說是不可見的(參見《第5章 - Scala 基礎面向對象編程》的”可見域規(guī)則“)。被重寫的click 方法會增加count。它只在count 小于等于maxAllowed 數目的時候調用super.click()。

這里是展示ObservableClicks 和VetoableClicks 一起工作的Specs 對象。注意每一個Trait 都需要一個單獨的with 關鍵字,和Java 對于implements 指令只要一個關鍵字和用逗號隔開名稱的實現方式不一樣。

  1. // code-examples/Traits/ui2/button-clickable-observer-vetoable-spec.scala  
  2. package ui2  
  3. import org.specs._  
  4. import observer._  
  5. import ui.ButtonCountObserver  
  6. object ButtonClickableObserverVetoableSpec extends Specification {  
  7.   "A Button Observer with Vetoable Clicks" should {  
  8.     "observe only the first button click" in {  
  9.       val observableButton =  
  10.           new Button("Okay") with ObservableClicks with VetoableClicks  
  11.       val buttonClickCountObserver = new ButtonCountObserver  
  12.       observableButton.addObserver(buttonClickCountObserver)  
  13.       for (i <- 1 to 3) observableButton.click()  
  14.       buttonClickCountObserver.count mustEqual 1  
  15.     }  
  16.   }  
  17. }  
  18.  
  19.  
  20.  

觀察者的計數應該是1。observableButton 有下面的語句聲明,

  1. new Button("Okay") with ObservableClicks with VetoableClicks  
  2.  

我們可以推斷VetoableClicks 重寫的click 在ObservableClicks 重寫的click 之前被調用。大概地講,因為我們的匿名類沒有定義自己的click,所以這個方法會按照聲明從右到左開始尋找。實際上比這個更復雜,我們會在《第7章 - Scala 對象系統》的”對象結構的線性化“ 章節(jié)討論。

同時,如果我們把使用Trait 的順序反過來會發(fā)生什么呢?

  1. // code-examples/Traits/ui2/button-vetoable-clickable-observer-spec.scala  
  2. package ui2  
  3. import org.specs._  
  4. import observer._  
  5. import ui.ButtonCountObserver  
  6. object ButtonVetoableClickableObserverSpec extends Specification {  
  7.   "A Vetoable Button with Click Observer" should {  
  8.     "observe all the button clicks, even when some are vetoed" in {  
  9.       val observableButton =  
  10.           new Button("Okay") with VetoableClicks with ObservableClicks  
  11.       val buttonClickCountObserver = new ButtonCountObserver  
  12.       observableButton.addObserver(buttonClickCountObserver)  
  13.       for (i <- 1 to 3) observableButton.click()  
  14.       buttonClickCountObserver.count mustEqual 3  
  15.     }  
  16.   }  
  17. }  
  18.  

現在觀察者的計數應該是3。ObservableClicks 現在比VetoableClicks 擁有更高的優(yōu)先級,所以點擊的計數會增加,即使有些點擊在接下來的動作中被否決!

所以,為了防止Trait 之間互相影響導致不可預料的后果,聲明的順序很重要。也許另外一個教訓是,把對象分拆成太多細密的Traits 可能會值得你代碼的執(zhí)行變得復雜費解。

把你的程序分割成小的,個所有長的Trait 是個創(chuàng)建可重用,可伸縮的抽象和”組件“的強大方式。復雜的行為可以通過聲明Trait 的組合來完成。我們會在《第13章 - 應用程序設計》的“可伸縮的抽象”中更多地探索這個概念。

構造Traits

Traits 不支持輔助構造函數,它們也不支持在主構造函數,Trait 的主體里的參數列表。Traits 可以繼承類或者其它Trait。然而,因為它們不能給父類的構造函數傳遞參數(哪怕是字面值),所以Traits 只能繼承有無參數的主/副構造函數的類。

然而,不像類,Trait 的主體在每次使用Trait 創(chuàng)建一個實例的時候都會被執(zhí)行,正如下面的腳本所演示。

  1. // code-examples/Traits/Trait-construction-script.scala  
  2. Trait T1 {  
  3.   println( "  in T1: x = " + x )  
  4.   val x=1 
  5.   println( "  in T1: x = " + x )  
  6. }  
  7. Trait T2 {  
  8.   println( "  in T2: y = " + y )  
  9.   val y="T2" 
  10.   println( "  in T2: y = " + y )  
  11. }  
  12. class Base12 {  
  13.   println( "  in Base12: b = " + b )  
  14.   val b="Base12" 
  15.   println( "  in Base12: b = " + b )  
  16. }  
  17. class C12 extends Base12 with T1 with T2 {  
  18.   println( "  in C12: c = " + c )  
  19.   val c="C12" 
  20.   println( "  in C12: c = " + c )  
  21. }  
  22. println( "Creating C12:" )  
  23. new C12println( "After Creating C12" )  
  24.  

用scala 命令運行這段腳本會得到以下輸出。

  1. Creating C12:  
  2.   in Base12: b = null 
  3.   in Base12: b = Base12 
  4.   in T1: x = 0 
  5.   in T1: x = 1 
  6.   in T2: y = null 
  7.   in T2: y = T2 
  8.   in C12: c = null 
  9.   in C12: c = C12 
  10. After Creating C12  
  11.  
  12.     
  13.  

注意類和Trait 構造函數的調用順序。因為C12 的聲明繼承自Base12 with T1 with T2,這個類結構的構造順序是從左到右的,從基類Base12 開始,接著是Traits T1 和T2,最后是C12 的構造主體。(對于構造任意復雜的結構,參見《第7章 - Scala 對象系統》的“對象結構的線性化”章節(jié)。)

所以,雖然你不能傳遞構造參數給Trait,你可以用默認值初始化字段,或讓它們繼續(xù)抽象。我們實際上在前面的Subject Trait 中見過, Subject.observers 字段被初始化為一個空列表。

如果Trait 的一個具體字段沒有合適的默認值,那么就沒有一個“萬無一失”的方式來初始化這個值了。所有的其它方法都需要這個Trait 的用戶的一些特別步驟,這很容易發(fā)生錯誤,因為他們可能會做錯甚至忘記去做。也許這個字段應該繼續(xù)作為抽象字段,這樣類和其它Trait 使用它的時候會被強制定義一個合適的值。我們會在《第6章 - Scala 高級面向對象編程》中詳細討論重寫抽象和具體成員。

另外一個解決方案是把這個字段轉移到一個單獨的類中,這樣構造過程可以保證用戶可以提供正確的初始化數據。這樣也許應該說這整個Trait 實際上應該是一個類,這樣你才能定義一個構造函數來初始化這個字段。

類還是Trait?

當我們考慮是否一個“概念”應該成為一個Trait 或者一個類的時候,記住作為混入的Trait 對于“附屬”行為來說最有意義。如果你發(fā)現某一個Trait 經常作為其它類的父類來用,導致子類會有像父Trait 那樣的行為,那么考慮把它定義為一個類吧,讓這段邏輯關系更加清晰。(我們說像。。。的行為,而不是是。。。,因為前者是繼承更精確的定義,基于Liskov Substitution Principle -- 例如參見 [Martin2003]。)

提示

在Trait 里避免不能用合適的默認值初始化的具體字段。使用抽象字段,或者把這個Trait 轉換成一個有構造函數的類。當然,無狀態(tài)Trait 沒有初始化的問題。

一個實例應該從構造過程結束開始,永遠都在一個已知的有效的狀態(tài)下,這是優(yōu)秀的面向對象設計的基本原則。

概括,及下章預告

在這一章,我們學習了如何使用Trait 來封裝和共享類之間正交的關注點。我們也學習了何時,以及如何使用Trait,如果“堆疊”多個Trait,以及初始化Trait 的成員的規(guī)則。

在下一章,我們會探索Scala 編程中的面向對象基礎。即使你是一個面向對象編程的老手,你也會希望通過閱讀下面的章節(jié)來理解Scala 面向對象方法的方方面面。

51CTO推薦專題

[[16158]]

【編輯推薦】

  1. 《Scala編程指南》第一章
  2. 《Scala編程指南》第二章 
  3. 《Scala編程指南》第三章 
  4. 51CTO專訪Scala創(chuàng)始人:Scala拒絕學術化
  5. “Scala” 一個有趣的語言
責任編輯:佚名 來源: 51CTO
相關推薦

2010-09-14 15:34:41

Scala

2010-11-17 11:31:22

Scala基礎面向對象Scala

2010-09-14 13:22:17

Scala編程指南Scala

2010-09-14 14:28:58

Scala

2009-07-15 10:14:25

Scala并發(fā)性

2018-09-26 11:12:35

iOS蘋果功能

2009-09-09 14:09:35

Scala Trait

2010-03-11 10:34:22

Scala

2011-06-28 11:06:16

Scala

2009-09-24 09:41:00

Scala講座Scala

2011-08-02 09:38:25

IOS 用戶設計

2009-12-11 10:45:00

Scala講座類型系統功能

2009-07-09 00:25:00

ScalaSet類Map類

2009-07-09 00:25:00

ScalaListTuple

2016-12-30 13:43:35

異步編程RxJava

2017-01-12 14:55:50

JavaScript編程

2011-12-12 11:16:02

iOS并發(fā)編程

2011-07-03 10:16:45

Core Animat

2010-07-20 13:32:25

Perl編程格式

2009-08-27 12:00:40

ibmdwJava
點贊
收藏

51CTO技術棧公眾號

久久精品视频一区二区三区| 在线日本高清免费不卡| 欧美色成人综合| www.黄色网址.com| 国产综合在线播放| 视频在线观看一区| 久久国产精品免费视频| 中文字幕99页| 精品国模一区二区三区| ●精品国产综合乱码久久久久| 97人人模人人爽人人喊38tv| 五月天婷婷久久| 水蜜桃精品av一区二区| 精品国产乱码久久久久久久久| 久久精品.com| 黄页视频在线播放| 91欧美激情一区二区三区成人| 国产欧美一区二区三区在线看| 黄色小视频在线免费看| 清纯唯美亚洲综合一区| 欧美一区二区成人| 嫩草av久久伊人妇女超级a| av免费在线免费| 国产欧美日韩麻豆91| 成人免费91在线看| 91超薄丝袜肉丝一区二区| 国产欧美亚洲一区| 久久国产精品影视| 99久久久无码国产精品衣服| 成人涩涩网站| 91精品国产免费| wwwxxx黄色片| 蜜桃视频在线观看播放| 亚洲九九爱视频| 日韩精品久久一区二区三区| 肥臀熟女一区二区三区| 久久99久久久欧美国产| 日韩av大片免费看| 日本在线视频免费| 欧美特黄一区| 色悠悠国产精品| 91成人破解版| 亚洲婷婷伊人| 亚洲二区在线播放视频| 亚洲 自拍 另类 欧美 丝袜| **国产精品| 欧美日韩色综合| 无码内射中文字幕岛国片| 中文一区一区三区高中清不卡免费| 亚洲男人的天堂网| 99re8这里只有精品| 日本三级在线视频| 中文字幕永久在线不卡| 亚洲精品在线免费| 99re热久久这里只有精品34| 国产欧美日韩一区二区三区在线观看| 欧美性色黄大片人与善| 青青草在线免费视频| 久久这里只有精品视频网| 免费国产一区二区| 免费黄色片在线观看| 久久综合国产精品| 日韩国产一区久久| 99reav在线| 一区二区三区中文字幕| 嫩草影院中文字幕| av小说在线播放| 色综合久久综合网97色综合| 国内外免费激情视频| 日韩a**中文字幕| 欧美日韩一级黄| 日韩欧美亚洲另类| 精品一区二区三区中文字幕视频 | 97人妻一区二区精品视频| 国产精品外国| 国产精品视频成人| 国产毛片毛片毛片毛片| 粉嫩久久99精品久久久久久夜| 粉嫩精品一区二区三区在线观看| 手机av免费在线观看| 久久综合色播五月| 亚洲一区3d动漫同人无遮挡 | 精品中文一区| 国产一区二区三区在线视频 | 能看毛片的网站| 风间由美中文字幕在线看视频国产欧美 | 国产精品视频九色porn| 在线视频欧美一区| 欧美巨大xxxx做受沙滩| 色综合天天天天做夜夜夜夜做| 国产精品久久久毛片| 精品视频在线一区| 日韩大陆毛片av| 1024在线看片| 红桃视频亚洲| 国产国语videosex另类| 国产原创中文av| 99免费精品在线| 亚洲欧美日韩精品久久久| 人妖欧美1区| 色悠久久久久综合欧美99| 黄色aaaaaa| 欧美人妖视频| 不卡av在线网站| av大全在线观看| 国产一区二区三区不卡在线观看| 久久久久se| 九色porny丨首页在线| 欧美日韩国产限制| 日本一本在线视频| 国产一区二区电影在线观看| 欧美人成在线视频| 中文字幕av第一页| caoporn国产精品| 色撸撸在线观看| 日韩av中字| 精品国产乱码久久久久久久| 99热99这里只有精品| 亚洲久久视频| 91av免费看| 极品白浆推特女神在线观看| 亚洲aⅴ怡春院| 欧洲美女亚洲激情| 精品一区二区三| 国产91精品不卡视频| 国产又色又爽又黄又免费| 国产香蕉久久精品综合网| 无码粉嫩虎白一线天在线观看 | 国产在线精品一区二区三区》| 欧美13一16娇小xxxx| 91福利资源站| www.超碰97| 亚洲精品色图| 99久久精品免费看国产一区二区三区| 777电影在线观看| 色8久久精品久久久久久蜜| 中文字幕精品视频在线| 亚洲一级影院| 999视频在线免费观看| 亚洲成人三级| 欧美日韩在线免费视频| 公侵犯人妻一区二区三区| 一本色道久久综合| 国产一区二区三区高清| 99热99re6国产在线播放| 日韩色在线观看| 国产精品嫩草影院俄罗斯| 麻豆一区二区99久久久久| 先锋影音网一区| 国产成人福利夜色影视| 在线日韩日本国产亚洲| 99久久久无码国产精品免费蜜柚| 久久噜噜亚洲综合| 日韩免费高清在线| 欧美美女视频| 国产精品视频一区国模私拍| 都市激情一区| 欧美日韩亚洲丝袜制服| 久久久久人妻一区精品色| 美女国产一区二区三区| 一区二区三区四区五区视频| 日韩毛片免费视频一级特黄| www.精品av.com| 99久久亚洲精品日本无码| 亚洲制服丝袜在线| 中文字幕天堂网| 久热精品视频| 一区精品在线| 亚州一区二区| 91大神在线播放精品| 久久久资源网| 欧美精品免费视频| 亚洲色图27p| 国产精品一区二区久久不卡 | 国产蜜臀在线| 国产视频精品在线| 中文字幕免费播放| 亚洲私人黄色宅男| www国产视频| 蜜臀av性久久久久蜜臀aⅴ四虎| www亚洲国产| 国产毛片精品| 国产精品九九九| 超碰公开在线| 日韩大片在线观看视频| 亚洲天堂网视频| 一区二区三区欧美久久| 青青草视频成人| 久久国产麻豆精品| xxxx18hd亚洲hd捆绑| 国产一区二区三区电影在线观看| 91手机视频在线观看| 欧产日产国产精品视频| 日韩天堂在线视频| 五月婷婷综合久久| 欧美日韩国产一区| 免费日韩一级片| 中文字幕制服丝袜成人av| 亚洲图片欧美另类| 蜜臀av性久久久久蜜臀aⅴ四虎 | 国产精品一区二区日韩| 在线视频中文亚洲| 欧美一级特黄aaaaaa大片在线观看 | 国产精品一二二区| 日韩 欧美 高清| 欧美特黄一级| 亚洲午夜精品久久| 天美av一区二区三区久久| 成人免费福利视频| 国模套图日韩精品一区二区| 久久久精品日本| 国产三级在线看| 亚洲国产精品久久久久久| 一级片aaaa| 日本韩国一区二区| 国产欧美日韩另类| 亚洲男人的天堂在线观看| 亚洲一级黄色录像| 久久蜜桃一区二区| 疯狂揉花蒂控制高潮h| 国产成人免费视| 国产无色aaa| 日本欧美一区二区三区乱码| 内射国产内射夫妻免费频道| 欧美精品一级| 免费观看国产视频在线| 日本不卡高清| 日本在线观看一区二区| 欧美激情极品| 国产一区二区在线网站| 天堂精品久久久久| 91在线观看欧美日韩| 九九九精品视频| 国产精品国产亚洲伊人久久 | 中文字幕一区二区三区乱码图片| 特级西西444www大精品视频| 夜夜春成人影院| 国产一区二区不卡视频在线观看| 超碰成人福利| 高清免费日韩| a级日韩大片| 成人动漫视频在线观看免费| 亚洲精品18| 成人情视频高清免费观看电影| 国产激情精品一区二区三区| 成人在线免费观看视视频| 亚州精品国产| 91系列在线播放| 激情久久免费视频| 3d精品h动漫啪啪一区二区| 国产一区 二区| 亚洲一区二区中文| 日本亚洲视频| 岛国视频一区| 任我爽精品视频在线播放| 久久久久久欧美精品色一二三四 | 亚洲综合五月天| 91久久夜色精品国产按摩| 中文字幕在线亚洲三区| 亚洲字幕久久| 91午夜在线观看| 国产日韩欧美一区| 别急慢慢来1978如如2| 久久精品国产亚洲一区二区三区| 欧美性受xxxxxx黑人xyx性爽| 国产一区二区三区四区在线观看| 日本一二三区在线| 成人激情小说乱人伦| 女尊高h男高潮呻吟| 国产欧美日韩另类一区| 永久看片925tv| 亚洲成国产人片在线观看| 国产精品人人人人| 欧美综合天天夜夜久久| 国产精品人妻一区二区三区| 欧美r级电影在线观看| 天堂а√在线8种子蜜桃视频| 亚洲视频在线观看| 国产成人无吗| 51视频国产精品一区二区| 色8久久影院午夜场| 91精品视频播放| 欧美绝顶高潮抽搐喷水合集| 亚洲成人一区二区三区| 国产精品www.| 五月婷婷深爱五月| 国产成人午夜视频| 三级网站在线免费观看| 亚洲欧美日本韩国| 狠狠躁夜夜躁人人爽天天高潮| 欧洲一区在线观看| www.亚洲欧美| 亚洲网址你懂得| 色yeye免费人成网站在线观看| 国产91|九色| 国产一区二区三区国产精品| 久久国产精品免费一区| 午夜国产一区二区| 国产91在线视频观看| 韩国女主播成人在线观看| 日韩乱码人妻无码中文字幕久久| 亚洲男人的天堂在线观看| 日本视频免费观看| 日韩欧美国产成人一区二区| 久久经典视频| 久久久久久网站| 欧美亚洲黄色| 精品午夜一区二区| 亚洲国产精品综合久久久| 日本在线观看a| 粉嫩av一区二区三区在线播放 | 青青草国产精品一区二区| 中文字幕日韩亚洲| 日韩国产高清一区| 亚洲一区亚洲| 精品国产aⅴ一区二区三区东京热| 国产亚洲人成网站| 日韩av在线播放观看| 欧美一区二区不卡视频| 国产98在线| 人九九综合九九宗合| gogo久久日韩裸体艺术| 椎名由奈jux491在线播放| 久久一区视频| 精品中文字幕在线播放| 亚洲精品乱码久久久久久| 又骚又黄的视频| 国产一区二区成人| 亚洲天堂av影院| 精品在线视频一区二区| 在线免费观看欧美| 国产调教打屁股xxxx网站| 综合电影一区二区三区 | 日本最新不卡在线| 欧美 日本 国产| 欧美日韩色婷婷| 天堂v在线观看| 久久久久五月天| 成人爽a毛片| 国产 日韩 亚洲 欧美| 成人性色生活片免费看爆迷你毛片| 伊人在线视频观看| 欧美一区二区视频在线观看2020 | 欧美精品一区二区三区中文字幕| www.com毛片| 久久婷婷综合激情| 69视频免费看| 亚洲无线码在线一区观看| 大胆人体一区二区| 欧美日韩三区四区| 日韩成人免费看| 成人欧美一区二区三区黑人一| 欧美三级在线播放| 婷婷免费在线视频| 91在线观看免费观看| 狠狠干成人综合网| 成人在线视频免费播放| 精品国产老师黑色丝袜高跟鞋| 天堂网www中文在线| 国产精品吹潮在线观看| 日韩免费特黄一二三区| 午夜激情影院在线观看| 一区二区视频在线| 欧美熟妇乱码在线一区| 1769国产精品| 日韩极品一区| 国产黄色一区二区三区| 午夜精品福利一区二区三区av | 男人天堂1024| 国产亚洲精品bt天堂精选| 中文字幕av网站| 欧美成人亚洲成人| 欧美大奶一区二区| 欧美日韩在线免费播放| 一区精品在线播放| 欧美性猛交 xxxx| 国产精品美女免费| 欧美大片一区| 国产成人av一区二区三区不卡| 欧美色图片你懂的| 色爱综合区网| 欧美最大成人综合网| 精品一区二区三区在线播放| 久久午夜鲁丝片午夜精品| 亚洲免费电影一区| 成人在线视频国产| 99色精品视频| 亚洲男人的天堂在线aⅴ视频| 天天av综合网| 成人久久一区二区三区| 中文在线一区| 丰满少妇被猛烈进入一区二区| 日韩精品一区二区视频| 亚洲三级在线| 波多野结衣家庭教师在线| 一区免费观看视频| 嫩草在线播放| www日韩av| 久久精品国产亚洲一区二区三区| 国产污视频在线看|