2012-11-30 4 views
20

Allgemeine Stil Frage.Scala Objekte und der Anstieg von Singletons

Da ich besser bin, Funktionscode zu schreiben, werden mehr meiner Methoden reine Funktionen. Ich finde, dass viele meiner "Klassen" (im lockeren Sinne eines Containers mit Code) staatsfrei werden. Daher mache ich sie Objekte statt Klassen, da sie nicht instanziiert werden müssen.

Jetzt in der Java-Welt, würde eine Klasse voller "statischen" Methoden eher seltsam erscheinen, und wird im Allgemeinen nur für "Helfer" -Klassen verwendet, wie Sie sehen mit Guava und Common- * und so weiter.

Also meine Frage ist, in der Scala-Welt, hat viel Logik in "Objekte" und nicht "Klassen" ganz normal, oder gibt es eine andere bevorzugte Idiom.

+0

warten, wenn sie Zustand frei sind, warum sind sie sogar Objekte und nicht nur Funktionen? – RonaldBarzell

+3

nicht benötigen Sie die Funktionen im Inneren etwas zu setzen, das ist meine Frage. Ich habe eine Reihe verwandter Funktionen, sie befinden sich in einem Objekt, einfach als Container. Ist das in Scala die beste Vorgehensweise oder nicht? – monkjack

+0

Oh ok. Ich habe es falsch verstanden. Danke für die Abklärung. – RonaldBarzell

Antwort

16

Wie Sie in Ihrem Titel erwähnen, sind Objekte Singleton-Klassen, keine Klassen mit statischen Methoden, wie Sie im Text Ihrer Frage erwähnen.

Und es gibt ein paar Dinge, die Scala-Objekte besser als statische UND Singletons in Java-Welt machen, also ist es ziemlich "normal", sie in Scala zu verwenden.

Für eine Sache, im Gegensatz zu statischen Methoden sind Objektmethoden polymorph, so dass Sie leicht Objekte als Abhängigkeiten injizieren:

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 

Dies sich ohne enge Kopplung nutzbar macht und ohne globalen Zustand zu fördern (die zwei von die Probleme, die sowohl statischen Methoden als auch Singletons zugeschrieben werden).

Dann gibt es die Tatsache, dass sie mit all den Boilerplate, die Singletons so hässlich und unidiomatisch aussieht in Java machen. Dies ist meiner Meinung nach ein oft übersehener Punkt und ein Teil dessen, was Singletons in Java so verpönt macht, selbst wenn sie staatenlos sind und nicht als globaler Staat verwendet werden.

Auch das Boilerplate, das Sie in allen Java-Singletons wiederholen müssen, gibt der Klasse zwei Verantwortlichkeiten: Sicherstellen, dass es nur eine Instanz von sich gibt und tut, was es tun soll. Die Tatsache, dass scala eine deklarative Art und Weise hat, etwas als Singleton zu spezifizieren, entlastet die Klasse und den Programmierer davon, das Prinzip der einheitlichen Verantwortung zu brechen. In Scala wissen Sie, dass ein Objekt ein Singleton ist, und Sie können nur darüber nachdenken, was es tut.

+5

Ich denke, es ist nicht genug betont werden kann, dass ein scala Objekt etwas verlängern kann, die die Grundlage es für die Verwendung mit der Dependency Injection ist Idiom. Danke, dass du ein Beispiel gegeben hast! –

+0

@ChristianSchlichtherle Danke, ich wünschte nur, ich ein besseres Beispiel hatte. Ich mag nicht viel die erfundene, die ich gab ... –

2

Ja, ich würde sagen, es ist normal.

Für die meisten meiner Klassen erstelle ich ein Companion-Objekt, um dort einige Initialisierungs-/Validierungslogik zu behandeln. Zum Beispiel, anstatt eine Ausnahme zu werfen, wenn die Validierung von Parametern in einem Konstruktor fehlschlägt ist es möglich, ein Option oder Either in dem Begleiter zurückzukehren Objekte anwenden-Methode:

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) 

Im Begleitobjekt kann man viel hinzufügen zusätzliche Logik, z. B. ein Cache für unveränderliche Objekte.

Objekte sind auch gut für die Signale zwischen den Instanzen zu senden, wenn es keine Notwendigkeit, Nachrichten auch senden ist:

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

Ein weiterer Anwendungsfall für Objekte ist es, den Umfang der Elemente zu reduzieren:

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