2012-11-30 30 views
20

一般風格的問題。Scala對象和單身人士的興起

隨着我越來越擅長編寫功能代碼,我的更多方法正在變成純粹的功能。我發現很多我的「類」(鬆散的代碼容器)正在成爲免費的狀態。因此我使它們成爲對象而不是類,因爲不需要實例化它們。現在在Java世界裏,擁有一個充滿「靜態」方法的類似乎很奇怪,而且通常只用於「助手」類,就像您在Guava和Commons- *中看到的那樣。

所以我的問題是,在斯卡拉的世界裏,有很多邏輯裏面的「對象」而不是「類」很普通,或者是有另一種優選的習慣用法。

+0

等等,如果它們是無狀態的,爲什麼它們甚至是對象而不是簡單的函數? – RonaldBarzell

+3

你不需要把這些功能放在一些東西里面,這是我的問題。我有一堆相關的函數,它們在一個Object中,只是作爲一個容器。這是否被認爲是Scala中的最佳實踐? – monkjack

+0

哦,好的。我誤解了。謝謝你的澄清。 – RonaldBarzell

回答

16

正如你在標題中提到的那樣,對象是單例類,而不是你在問題文本中提到的靜態方法類。

還有一些東西使得scala對象比java-world中的靜態和單例更好,所以在scala中使用它們是非常「正常」的。

一方面,不同於靜態方法,對象的方法是多態的,因此您可以輕鬆地將對象作爲依賴關係:

scala> trait Quack {def quack="quack"} 
defined trait Quack 

scala> class Duck extends Quack 
defined class Duck 

scala> object Quacker extends Quack {override def quack="QUAACK"} 
defined module Quacker 

// MakeItQuack expects something implementing Quack 
scala> def MakeItQuack(q: Quack) = q.quack 
MakeItQuack: (q: Quack)java.lang.String 

// ...it can be a class 
scala> MakeItQuack(new Duck) 
res0: java.lang.String = quack 

// ...or it can be an object 
scala> MakeItQuack(Quacker) 
res1: java.lang.String = QUAACK 

這使得他們可以使用沒有緊密結合,並沒有促進全球狀態(這是兩個這些問題通常歸因於靜態方法和單例)。

然後有一個事實,他們取消了所有使單身人士如此醜陋和unidiomatic-看在java中的樣板。在我看來,這是一個經常被忽視的問題,也是單身人士在java中如此不悅的部分原因,即使他們是無國籍人士並且不被用作全球國家。

此外,您必須在所有java單例中重複的樣板文件賦予了類的兩個職責:確保只有一個本身的實例並執行它應該執行的任何操作。 scala具有聲明式的方式來指定某件事是單例式的,這一事實使得班級和程序員免於違反單一責任原則。在scala中,你知道一個對象是一個單例,你可以推理它的功能。

+5

我認爲它不能強調一個scala對象可以擴展任何東西,這是與依賴注入一起使用它的基礎成語。謝謝你舉個例子! –

+0

@ChristianSchlichtherle謝謝,我只是希望我有一個更好的例子。我不喜歡我給的那些人爲的東西...... –

2

是的,我會說這是正常的。

對於我的大部分類,我創建了一個伴侶對象來處理那裏的一些初始化/驗證邏輯。例如,而不是在構造函數中,如果參數驗證失敗時拋出異常,可以返回在同伴的OptionEither對象適用法:

class X(val x: Int) { 
    require(x >= 0) 
} 
// ==> 
object X { 
    def apply(x: Int): Option[X] = 
    if (x < 0) None else Some(new X(x)) 
} 
class X private (val x: Int) 

在同伴對象可以增添不少額外的邏輯,如不可變對象的緩存。

對象也是好的實例之間發送信號,如果沒有必要還發送消息:

object X { 
    def doSomething(s: String) = ??? 
} 
case class C(s: String) 

class A extends Actor { 
    var calculateLater: String = "" 
    def receive = { 
    case X => X.doSomething(s) 
    case C(s) => calculateLater = s 
    } 
} 

另一種使用情況的對象是減少元素的範圍:

// traits with lots of members 
trait A 
trait B 
trait C 

trait Trait { 

    def validate(s: String) = { 
    import validator._ 
    // use logic of validator 
    } 

    private object validator extends A with B with C { 
    // all members of A, B and C are visible here 
    } 
} 

class Class extends Trait { 
    // no unnecessary members and name conflicts here 
}