2012-11-30 9 views
20

Ogólne pytanie dotyczące stylu.Obiekty Scala i powstanie singletonów

W miarę jak doskonalę się w pisaniu kodu funkcjonalnego, coraz więcej moich metod staje się czystymi funkcjami. Uważam, że wiele moich "klas" (w luźnym znaczeniu kontenera kodu) staje się wolne od stanu. Dlatego robię je obiektami zamiast klas, ponieważ nie ma potrzeby ich tworzenia.

Teraz, w świecie java, posiadanie klasy pełnej "statycznych" metod wydaje się dość dziwne i jest zwykle używane tylko dla klas "pomocników", jak widzisz z Guava i Commons- * i tak dalej.

Moje pytanie brzmi, czy w świecie Scala jest dużo logiki wewnątrz "obiektów", a nie "klas" całkiem normalnych, czy jest tam inny preferowany idiom.

+0

Czekaj, jeśli są one wolne od państwa, dlaczego są one nawet obiektami, a nie tylko funkcjami? – RonaldBarzell

+3

Nie musisz umieszczać funkcji w czymś, co jest moim pytaniem. Mam kilka powiązanych funkcji, są one wewnątrz obiektu, po prostu jako pojemnik. Czy jest to uważane za najlepszą praktykę w Scali, czy nie. – monkjack

+0

Oh ok. Źle zrozumiałem. Dziękuję za wyjaśnienie. – RonaldBarzell

Odpowiedz

16

Jak wspominasz w swoim tytule, obiekty są klasami singleton, a nie klasami metod statycznych, o czym wspominasz w tekście swojego pytania.

I jest kilka rzeczy, które sprawiają, że obiekty scala są lepsze od obu statycznych i singletonów w świecie java, więc używanie ich w scala jest całkiem "normalne".

Po pierwsze, w przeciwieństwie do metody statyczne, metody obiektów są polimorficzny, dzięki czemu można łatwo wprowadzić obiektów jak zależnościami:

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 

To sprawia, że ​​można je było stosować bez napięty sprzęgła i bez promowania stan globalnej (które są dwa zagadnienia ogólnie przypisywane zarówno metodom statycznym, jak i pojedynczym).

Potem jest fakt, że usuwają wszystkie elementy, które sprawiają, że singletony są tak brzydkie i jednoznacznie wyglądające w języku Java. Jest to często pomijany punkt, moim zdaniem, i część tego, co sprawia, że ​​singletony są tak źle postrzegane w Javie, nawet gdy są bezpaństwowcami i nie są używane jako globalne państwo.

Ponadto, wzorzec, który trzeba powtarzać we wszystkich singletach java, nadaje klasie dwie obowiązki: zapewnienie, że istnieje tylko jeden przypadek i robi to, co powinien. Fakt, że scala ma deklaratywny sposób określenia, że ​​coś jest singletonem, zwalnia klasę i programistę z łamania zasady jednej odpowiedzialności. W scala wiesz, że obiekt jest singletonem i możesz po prostu uzasadnić, co robi.

+5

Myślę, że nie można podkreślić, że obiekt scala może rozszerzyć wszystko, co jest podstawą do użycia go z wtryskiem zależności idiom. Dzięki za podanie przykładu! –

+0

@ Christiansschlichtherle Dzięki, chciałbym tylko mieć lepszy przykład. Nie podoba mi się ta sztuczna, którą dałem ... –

2

Tak, powiedziałbym, że to normalne.

Dla większości moich klas tworzę obiekt towarzyszący, aby obsłużyć tam pewną logikę inicjalizacji/walidacji. Na przykład, zamiast rzucać wyjątek, jeśli walidacja parametrów nie w konstruktorze jest możliwe zwracać Option lub Either w towarzysza obiektów zastosować-metoda:

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) 

W obiekcie towarzysz można dodać wiele dodatkowa logika, taka jak pamięć podręczna dla niezmiennych obiektów.

obiekty są również dobre do przesyłania sygnałów między przypadkach, gdy nie ma potrzeby, aby również wysyłać wiadomości:

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 
    } 
} 

Kolejny przypadek użycia dla obiektów jest zmniejszenie zakresu elementów:

// 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 
}