2013-06-30 113 views
4

函數字面的我有一個類,它表示銷售訂單:分配在斯卡拉

class SalesOrder(val f01:String, val f02:Int, ..., f50:Date)

fXX領域是不同類型的。我面臨着創建我的訂單審計跟蹤的問題。給定兩個類的實例,我必須確定哪些字段已經改變。我想出了以下內容:

class SalesOrder(val f01:String, val f02:Int, ..., val f50:Date){ 

    def auditDifferences(that:SalesOrder): List[String] = { 

    def diff[A](fieldName:String, getField: SalesOrder => A) = 
     if(getField(this) != getField(that)) Some(fieldName) else None 

    val diffList = diff("f01", _.f01) :: diff("f02", _.f02) :: ... 
       :: diff("f50", _.f50) :: Nil 

    diffList.flatten 
    }  
} 

我想知道的編譯器與所有_.fXX功能是什麼:他們在實例化只是一次(靜態),並且可以通過我的類的所有實例共享,或將每次創建我的課程實例時都會將它們實例化?

我擔心的是,因爲我會使用很多SalesOrder實例,它可能會產生大量垃圾。我應該使用不同的方法嗎?

+1

您也可能會擔心在List中與陣列中創建'Some'對象或存儲字段更改。或者如果您必須爲'n'銷售訂單調用'diff'' n-1'次,您將如何重構審計跟蹤。如果您遇到性能問題,您現在編寫的方式將爲優化留下機會。 – huynhjl

回答

2

一解決這個問題的乾淨方法是使用標準庫的Ordering type class。例如:

class SalesOrder(val f01: String, val f02: Int, val f03: Char) { 
    def diff(that: SalesOrder) = SalesOrder.fieldOrderings.collect { 
    case (name, ord) if !ord.equiv(this, that) => name 
    } 
} 

object SalesOrder { 
    val fieldOrderings: List[(String, Ordering[SalesOrder])] = List(
    "f01" -> Ordering.by(_.f01), 
    "f02" -> Ordering.by(_.f02), 
    "f03" -> Ordering.by(_.f03) 
) 
} 

然後:

scala> val orderA = new SalesOrder("a", 1, 'a') 
orderA: SalesOrder = [email protected] 

scala> val orderB = new SalesOrder("b", 1, 'b') 
orderB: SalesOrder = [email protected] 

scala> orderA diff orderB 
res0: List[String] = List(f01, f03) 

你幾乎肯定不需要擔心你的原始配方的性能比較,不過這個版本(可以說)爲更好不相干的原因。

+0

我很好奇「不相關的原因」:-)使用'Ordering'比使用'!='比較不等式更好嗎? – Eduardo

1

是的,這會創建50個短暫的功能。我不認爲你應該擔心,除非你有明確的證據表明你的情況會導致性能問題。

不過,我會定義轉換SalesOrderMap[String, Any]的方法,那麼你就只需要

trait SalesOrder { 
    def fields: Map[String, Any] 
} 
def diff(a: SalesOrder, b: SalesOrder): Iterable[String] = { 
    val af = a.fields 
    val bf = b.fields 
    af.collect { case (key, value) if bf(key) != value => key } 
} 

如果字段名稱確實只是遞增的數字,你可以簡化

trait SalesOrder { 
    def fields: Iterable[Any] 
} 
def diff(a: SalesOrder, b: SalesOrder): Iterable[String] = 
    (a.fields zip b.fields).zipWithIndex.collect { 
    case ((av, bv), idx) if av != bv => f"f${idx + 1}%02d" 
    } 
+0

謝謝。這些字段有真實姓名。 fXX只是一個簡化。雖然使用Map [String,Any]解決了分配問題,但我認爲它具有丟失類型安全性的缺點:我無法確保分配給它們的值是正確的類型。我想我寧願有一個伴侶對象,我放置所有這些功能。 – Eduardo

+0

該地圖只是輔助,不會取代您的常規getter方法。對於等號操作,「Any」是否從地圖出來並不重要。基本上,我所說的是,如果你首先將所有字段值收集到一個序列中,那麼你可以只用一個閉包而不是50個閉包來比較它們。 –