Ist das die Absicht von Rakus Design?
Es ist fair zu sagen, dass Raku in diesem Bereich nicht völlig unbefangen ist. Ihre Frage berührt zwei Themen in Rakus Design, die beide eine kleine Diskussion wert sind.
Raku hat erstklassige l-Werte
Raku nutzt L-Werte in Hülle und Fülle als erstklassige Sache. Wenn wir schreiben:
has $.x is rw;
Die generierte Methode ist:
method x() is rw { $!x }
Das is rw
hier zeigt an, dass die Methode einen l-Wert zurückgibt, dh etwas, dem zugewiesen werden kann. Wenn wir also schreiben:
$obj.x = 42;
Dies ist kein syntaktischer Zucker: Es ist wirklich ein Methodenaufruf, und dann wird der Zuweisungsoperator auf das Ergebnis angewendet. Dies funktioniert, da der Methodenaufruf den Scalar
Container des Attributs zurückgibt , dem dann zugewiesen werden kann. Man kann die Bindung verwenden, um dies in zwei Schritte aufzuteilen, um zu sehen, dass es sich nicht um eine triviale syntaktische Transformation handelt. Zum Beispiel:
my $target := $obj.x;
$target = 42;
Würde dem Objektattribut zuweisen. Derselbe Mechanismus steckt hinter zahlreichen anderen Funktionen, einschließlich der Listenzuweisung. Zum Beispiel:
($x, $y) = "foo", "bar";
Konstruiert ein Konstrukt, List
das die Container $x
und enthält $y
, und dann iteriert der Zuweisungsoperator in diesem Fall jede Seite paarweise, um die Zuweisung durchzuführen. Dies bedeutet, dass wir dort rw
Objekt-Accessoren verwenden können:
($obj.x, $obj.y) = "foo", "bar";
Und alles funktioniert ganz natürlich. Dies ist auch der Mechanismus für die Zuweisung von Arrays und Hashes.
Sie können auch Proxy
einen L-Wert-Container erstellen, in dem Sie das Lese- und Schreibverhalten steuern können. So können Sie die Nebenaktionen in setzen STORE
. Jedoch...
Raku fördert semantische Methoden gegenüber "Setzern"
Wenn wir OO beschreiben, tauchen häufig Begriffe wie "Kapselung" und "Verstecken von Daten" auf. Die Schlüsselidee dabei ist, dass sich das Zustandsmodell innerhalb des Objekts - dh die Art und Weise, wie es die Daten darstellt, die es zur Implementierung seines Verhaltens (der Methoden) benötigt - frei entwickeln kann, beispielsweise um neue Anforderungen zu bewältigen. Je komplexer das Objekt ist, desto befreiender wird es.
Getter und Setter sind jedoch Methoden, die eine implizite Verbindung zum Status haben. Während wir vielleicht behaupten, dass wir das Ausblenden von Daten erreichen, weil wir eine Methode aufrufen und nicht direkt auf den Status zugreifen, ist meine Erfahrung, dass wir schnell an einem Ort landen, an dem externer Code Sequenzen von Setter-Aufrufen ausführt, um eine Operation zu erreichen - das heißt eine Form des Merkmals Neid Anti-Muster. Und wenn wir das tun , ist es ziemlich sicher, dass wir eine Logik außerhalb des Objekts haben, die eine Mischung aus Getter- und Setter-Operationen ausführt, um eine Operation zu erreichen. Eigentlich sollten diese Operationen als Methoden mit einem Namen verfügbar gemacht worden sein, der beschreibt, was erreicht wird. Dies wird noch wichtiger, wenn wir uns in einer gleichzeitigen Umgebung befinden. Ein gut gestaltetes Objekt ist an der Methodengrenze oft recht einfach zu schützen.
Das heißt, viele Verwendungen von class
sind wirklich Datensatz- / Produkttypen: Sie existieren, um einfach eine Reihe von Datenelementen zu gruppieren. Es ist kein Zufall, dass das .
Siegel nicht nur einen Accessor generiert, sondern auch:
- Legt fest, dass das Attribut durch die Standardobjektinitialisierungslogik festgelegt wird (dh a
class Point { has $.x; has $.y; }
kann als instanziiert werden Point.new(x => 1, y => 2)
), und rendert dies auch in der .raku
Dumpingmethode.
- Fügt das Attribut in das Standardobjekt ein
.Capture
, was bedeutet, dass wir es bei der Destrukturierung verwenden können (z sub translated(Point (:$x, :$y)) { ... }
. B. ).
Welches sind die Dinge, die Sie möchten, wenn Sie in einem prozeduraleren oder funktionaleren Stil schreiben und class
als Mittel zum Definieren eines Datensatztyps verwenden würden.
Das Raku-Design ist nicht für clevere Dinge in Setzern optimiert, da dies als schlecht zu optimierend angesehen wird. Es geht über das hinaus, was für einen Datensatztyp benötigt wird. In einigen Sprachen könnten wir argumentieren, dass wir die zugewiesenen Aufgaben validieren möchten, aber in Raku können wir uns dafür subset
Typen zuwenden . Wenn wir wirklich ein OO-Design erstellen, möchten wir gleichzeitig eine API mit aussagekräftigen Verhaltensweisen, die das Zustandsmodell verbirgt, anstatt in Getter / Setter zu denken, die dazu führen, dass die Zuordnung fehlschlägt Daten und Verhalten, was ohnehin ein wichtiger Punkt bei OO ist.