程式扎記: [Scala 小學堂] Scala Gossic : 了解更多 - 混入特徵 (作為共用實作的特徵)

標籤

2016年7月4日 星期一

[Scala 小學堂] Scala Gossic : 了解更多 - 混入特徵 (作為共用實作的特徵)

轉載自 這裡 
前言 : 
Scala 是一個可直譯、編譯、靜態、運行於 JVM 之上、可與 Java 互操作、融合物件導向編程特性與函式編程風格的程式語言. 特徵抽離共同的介面與實作,類別動態地繼承(extends)或具有(with)特徵. 

作為共用實作的特徵 : 
特徵不僅可規範抽象、沒有任何實作內容的方法,特徵中還可以撰寫有實作內容的方法。透過適當的設計,你可以將物件之間可能共用的實作抽離至特徵中,在必要時讓類別繼承或具有特徵,使得類別定義時得以精簡. 
舉個例子來說,你會定義以下的球類別,並定義了一些比較大小的方法 : 
- Ball.scala 代碼 :
  1. class Ball(val radius: Int) {  
  2.     def volume = 4 * Math.Pi * Math.pow(radius, 3) / 3  
  3.     def < (that: Ball) = this.radius - that.radius < 0  
  4.     def <=(that: Ball) = (this < that) || (this == that)  
  5.     def > (that: Ball) = !(this <= that)  
  6.     def >=(that: Ball) = !(this < that)  
  7.     override def equals(a: Any) = a match {  
  8.         case that: Ball => this.radius == that.radius;  
  9.         case _ => false  
  10.     }  
  11.     override def hashCode = 41 * radius  
  12. }  

事實上,比較大小順序這件事,許多物件都會用的到,仔細觀察以上的程式碼,你會發現可抽離的共用比較方法,你可以將之重新設計為特徵 : 
  1. trait Ordered {  
  2.     def compare(that: Any): Int  
  3.     def < (that: Any) = compare(that) < 0  
  4.     def <=(that: Any) = (this < that) || (this == that)  
  5.     def > (that: Any) = !(this <= that)  
  6.     def >=(that: Any) = !(this < that)  
  7. }  
特徵中除了 compare() 沒有實作之外,其它的方法都實作了。現在有了 Order 特徵,你可以在設計球類別時更為精簡,如果你需要彼此比較的功能,則只要繼承 Order 特徵並實作 compare() 方法即可以 : 
- Ball2.scala 代碼 :
  1. class Ball2(val radius: Int) extends Ordered {  
  2.     def volume = 4 * Math.Pi * Math.pow(radius, 3) / 3  
  3.     def compare(a: Any) = a match {  
  4.         case that: Ball2 => this.radius - that.radius;  
  5.         case _ => throw new IllegalArgumentException("不是球比什麼?")  
  6.     }  
  7.     override def equals(a: Any) = a match {  
  8.         case that: Ball2 => this.radius == that.radius;  
  9.         case _ => false  
  10.     }  
  11.     override def hashCode = 41 * radius  
  12. }  
  13.   
  14. val b1 = new Ball2(10)  
  15. val b2 = new Ball2(20)  
  16. println(b1 > b2)       // false  
  17. println(b1 >= b2)      // false  
  18. println(b1 < b2)       // true  
  19. println(b1 <= b2)      // true  
  20. println(b1 == b2)      // false  

事實上,Scala 確實有提供 scala.math.Ordered[A] 特徵來作比大小這種事 : 
  1. package scala  
  2. package math  
  3.   
  4. import scala.language.implicitConversions  
  5.   
  6. trait Ordered[A] extends Any with java.lang.Comparable[A] {  
  7.   
  8.   /** Result of comparing `this` with operand `that`. 
  9.    * 
  10.    * Implement this method to determine how instances of A will be sorted. 
  11.    * 
  12.    * Returns `x` where: 
  13.    * 
  14.    *   - `x < 0` when `this < that` 
  15.    * 
  16.    *   - `x == 0` when `this == that` 
  17.    * 
  18.    *   - `x > 0` when  `this > that` 
  19.    * 
  20.    */  
  21.   def compare(that: A): Int  
  22.   
  23.   /** Returns true if `this` is less than `that` 
  24.     */  
  25.   def <  (that: A): Boolean = (this compare that) <  0  
  26.   
  27.   /** Returns true if `this` is greater than `that`. 
  28.     */  
  29.   def >  (that: A): Boolean = (this compare that) >  0  
  30.   
  31.   /** Returns true if `this` is less than or equal to `that`. 
  32.     */  
  33.   def <= (that: A): Boolean = (this compare that) <= 0  
  34.   
  35.   /** Returns true if `this` is greater than or equal to `that`. 
  36.     */  
  37.   def >= (that: A): Boolean = (this compare that) >= 0  
  38.   
  39.   /** Result of comparing `this` with operand `that`. 
  40.     */  
  41.   def compareTo(that: A): Int = compare(that)  
  42. }  
Ordered[A] 可以在使用時宣告 A 的型態,例如,上例可以使用 Ordered[A] 來改寫如下,而結果仍相同 : 
- Ball3.scala 代碼 :
  1. class Ball3(val radius: Int) extends Ordered[Ball3] {  
  2.     def volume = 4 * Math.Pi * Math.pow(radius, 3) / 3  
  3.     def compare(that: Ball3) = this.radius - that.radius  
  4.     override def equals(a: Any) = a match {  
  5.         case that: Ball3 => this.radius == that.radius;  
  6.         case _ => false  
  7.     }  
  8.     override def hashCode = 41 * radius  
  9. }  
  10.   
  11. val b1 = new Ball3(10)  
  12. val b2 = new Ball3(20)  
  13. println(b1 > b2)       // false  
  14. println(b1 >= b2)      // false  
  15. println(b1 < b2)       // true  
  16. println(b1 <= b2)      // true  
  17. println(b1 == b2)      // false  

特徵可以定義抽象方法,這可以用來規範物件間必須共同實作的介面,特徵可以定義有具體實作的方法,這可以用來將一些可能會共用的實作或操作獨立出來,在必要時讓類別繼承或具有特徵,讓物件本身在定義時得以精簡.

沒有留言:

張貼留言

網誌存檔