Verwenden von Attributen mit Ruby-Code

Andreas Larsson / Folio Bilder / Getty Bilder

Sehen Sie sich irgendeinen objektorientierten Code an und alles folgt mehr oder weniger dem gleichen Muster. Erstellen Sie ein Objekt, rufen Sie einige Methoden für dieses Objekt auf und greifen Sie auf die Attribute dieses Objekts zu. Es gibt nicht viel anderes, was Sie mit einem Objekt tun können, außer es als Parameter an die Methode eines anderen Objekts zu übergeben. Aber wir haben es mit Attributen zu tun.

Attribute sind wie Instanzvariablen, auf die Sie über die Objektpunktnotation zugreifen können. Beispielsweise, Name der Person würde auf den Namen einer Person zugreifen. Genauso können Sie Attributen wie z person.name = "Alice". Dies ist ein ähnliches Feature für Membervariablen (wie in C ++), aber nicht ganz dasselbe. Es gibt hier nichts Besonderes, Attribute werden in den meisten Sprachen mit "Getter" und "Setter" implementiert oder mit Methoden, die Attribute von Instanzvariablen abrufen und setzen.

Ruby unterscheidet nicht zwischen Attribut-Getter und Setter und normalen Methoden. Da Rubys flexible Methode die Syntax aufruft, muss keine Unterscheidung getroffen werden. Beispielsweise, Name der Person und Name der Person() sind das gleiche, du rufst das an Name Methode mit Nullparametern. Einer sieht wie ein Methodenaufruf aus und der andere sieht wie ein Attribut aus, aber sie sind wirklich beide dasselbe. Sie rufen beide gerade an Name Methode. Ebenso kann jeder Methodenname, der mit einem Gleichheitszeichen (=) endet, in einer Zuweisung verwendet werden. Die Aussage person.name = "Alice" ist wirklich das Gleiche wie person.name = (Alice), obwohl zwischen dem Attributnamen und dem Gleichheitszeichen ein Leerzeichen steht, wird immer noch nur der Name aufgerufen Name = Methode.

Attribute selbst implementieren

Sie können Attribute einfach selbst implementieren. Durch die Definition von Setter- und Getter-Methoden können Sie beliebige Attribute implementieren. Hier ist ein Beispielcode, der das implementiert Name Attribut für eine Personenklasse. Es speichert den Namen in a @Name Instanzvariable, aber der Name muss nicht derselbe sein. Denken Sie daran, dass diese Methoden nichts Besonderes sind.

 #! / usr / bin / env ruby ​​class Person def initialisieren (name) @name = name end def name @name end def name = (name) @name = name end def say_hello setzt "Hallo, # {@ name}" ende Ende 

Eine Sache, die Sie sofort bemerken werden, ist, dass dies eine Menge Arbeit ist. Es ist eine Menge zu schreiben, nur um zu sagen, dass Sie ein Attribut namens wollen Name das greift auf die @Name Instanzvariable. Zum Glück bietet Ruby einige bequeme Methoden, die diese Methoden für Sie definieren.

Mit attr_reader, attr_writer und attr_accessor

Es gibt drei Methoden in der Modul Klasse, die Sie in Ihren Klassendeklarationen verwenden können. Denken Sie daran, dass Ruby keinen Unterschied zwischen Laufzeit und "Kompilierzeit" macht, und jeder Code innerhalb von Klassendeklarationen kann nicht nur Methoden, sondern auch Aufrufmethoden definieren. Anrufen der attr_reader, attr_writer und attr_accessor Methoden definieren wiederum die Setter und Getter, die wir im vorherigen Abschnitt definiert haben.

Das attr_reader Methode mag einfach, wie es klingt, wie es tun wird. Es nimmt eine beliebige Anzahl von Symbolparametern und definiert für jeden Parameter eine "Getter" -Methode, die die gleichnamige Instanzvariable zurückgibt. Also können wir unsere ersetzen Name Methode im vorherigen Beispiel mit attr_reader: Name.

Ähnlich, die attr_writer Methode definiert eine "Setter" -Methode für jedes Symbol, das an sie übergeben wird. Beachten Sie, dass das Gleichheitszeichen nicht Teil des Symbols sein muss, sondern nur der Attributname. Wir können das ersetzen Name = Methode aus dem vorherigen Beispiel mit einem Aufruf an attr_writer: Name.

Und wie erwartet, attr_accessor macht den Job von beiden attr_writer und attr_reader. Wenn Sie für ein Attribut sowohl einen Setter als auch einen Getter benötigen, ist es üblich, die beiden Methoden nicht separat aufzurufen und stattdessen aufzurufen attr_accessor. Wir könnten ersetzen beide das Name und Name = Methoden aus dem vorherigen Beispiel mit einem einzigen Aufruf an attr_accessor: Name.

 #! / usr / bin / env ruby ​​def Person attr_accessor: Name def initialize (Name) @name = Name end def say_hello setzt "Hallo, # {@ name}" Ende 

Warum Setter und Getter manuell definieren?

Warum sollten Sie Setter manuell definieren? Warum nicht die attr_ * Methoden jedes Mal? Weil sie die Kapselung unterbrechen. Kapselung ist der Grundsatz, der besagt, dass keine externe Entität unbeschränkten Zugriff auf den internen Zustand Ihrer Objekte haben sollte. Auf alles sollte über eine Schnittstelle zugegriffen werden, die verhindert, dass der Benutzer den internen Zustand des Objekts beeinträchtigt. Mit den oben genannten Methoden haben wir ein großes Loch in unsere Verkapselungswand geschlagen und konnten absolut alles für einen Namen festlegen, sogar offensichtlich ungültige Namen.

Eine Sache, die Sie oft sehen werden, ist das attr_reader wird verwendet, um schnell einen Getter zu definieren, aber ein benutzerdefinierter Setter wird definiert, da der interne Zustand des Objekts oft sein möchte lesen direkt aus dem internen Zustand. Der Setter wird dann manuell definiert und überprüft, ob der eingestellte Wert sinnvoll ist. Oder, vielleicht häufiger, wird überhaupt kein Setter definiert. Die anderen Methoden in der Klassenfunktion setzen die Instanzvariable auf andere Weise hinter dem Getter.

Wir können jetzt ein hinzufügen Alter und richtig implementieren a Name Attribut. Das Alter Attribut kann in der Konstruktormethode gesetzt werden, lesen Sie mit dem Alter Getter aber nur manipuliert mit dem have_birthday Methode, die das Alter erhöht. Das Name Das Attribut hat einen normalen Getter, aber der Setter stellt sicher, dass der Name großgeschrieben ist und die Form hat Vorname Nachname.

 #! / usr / bin / env Ruby-Klasse Person def initialize (Name, Alter) self.name = Name @age = Alter end attr_reader: name,: alter def name = (neuer_name) if neuer_name = ~ / ^ [AZ] [ az] + [AZ] [az] + $ / @name = neuer_name sonst puts "'# {neuer_name}' ist kein gültiger Name!" end end def have_birthday setzt "Alles Gute zum Geburtstag # {@ name}!" @age + = 1 end def whoami setzt "Du bist # {@ name}, Alter # {@ age}" Ende Ende p = Person.new ("Alice Smith", 23) # Wer bin ich? p.whoami # Sie heiratete p.name = "Alice Brown" # Sie versuchte ein exzentrischer Musiker zu werden p.name = "A" # Aber fehlgeschlagen # Sie wurde etwas älter p.have_birthday # Wer bin ich schon wieder? p.whoami 

Verwenden von Attributen mit Ruby-Code

Stimmenanzahl: 1

Also erstelle ich eine Klasse in Ruby:

Klasse Benutzer def initialisieren EndeEnde

Jetzt sage ich, dass ich ein Attribut erstellen möchte, das ein Hash mit einem Getter / Setter ist, ich bin verwirrt bezüglich der Optionen, die ich dabei habe.

Wenn ich das mache:

Klasse Benutzer attr_accessor: some_hashEnde

Aber ich möchte nicht, dass dieser Hash-Wert gleich Null ist, immer ein leerer Hash-Wert.

Ich bin verwirrt, wenn ich tun sollte:

def some_hash self.some_hash || = {}Ende

und TU:

def some_hash @some_hash || = {}Ende

Was ist der Unterschied?

Wenn ich den Attr_accessor nicht verwende, muss ich sowohl den Reader als auch den Writer (oder Getter / Setter) erstellen:

def Some_hash = ()Endedef some_hashEnde

Ich hoffe, dass jemand die Optionen aufräumen kann, die ich haben würde, um ein some_hash-Attribut zu erstellen, das ein Hash ist, und das nie eine Null zurückgibt, wenn es leer ist.

d. h. Verwendung von attr_accessor, manuelle Erstellung der Methoden und schließlich Verwendung von @some_hash und self.some_hash

fragte 21. März 12 um 17:33
Blankman

2 Antworten

Stimmenanzahl: 8 akzeptiert

attr_accessor: some_hash definiert Lese- und Schreibmethoden für das angegebene Attribut. Es entspricht dem:

Klasse Benutzer def some_hash @some_hash Ende def some_hash = (etwas_hash) @some_hash = etwas_hash EndeEnde

@some_hash verweist auf eine Instanzvariable des Objekts while etwas_hash und etwas_hash = sind Methoden. Die erste gibt den Wert der Variablen zurück, die zweite setzt sie.

Das Idiom self.some_hash || = {} ist äquivalent zu self.some_hash || self.some_hash = {}.

Boolesche Operatoren in Ruby-Kurzschluss, was den zweiten Ausdruck bedeutet (self.some_hash = {}) wird nicht ausgeführt, wenn der erste Ausdruck (self.some_hash) gibt einen wahren Wert zurück.

Die Methode:

def some_hash self.some_hash || = {}Ende

Ist eigentlich rekursiv, da es zu expandiert some_hash || self.some_hash = {}. Es wird sich selbst aufrufen, bis Sie einen Stapelüberlauf bekommen. Benutze das zweite Formular:

def some_hash @some_hash || = {}Ende

Da die Instanzvariable direkt gesetzt wird, haben Sie keine Probleme mit der Rekursion, noch müssen Sie eine Writer-Methode aufrufen. etwas_hash kann nie zurückkehren Null, weil wenn @some_hash ist Null, wird ihm ein leerer Hash zugewiesen, bevor die Methode zurückkehrt.

Dies wird übrigens auch als lazy initialization bezeichnet, da die Variable beim ersten Zugriff initialisiert wird und nicht beim Benutzer Instanz wird erstellt.

bearbeitet am 21. März 12 um 18:15
antwortete am 21. März 12 um 17:43
Matheus Moreira

Stimmenanzahl: 4

Abgesehen von Matheus 'Antwort, ein wenig mehr über Attribute:

Attribute sind Methoden. Sie definieren, wie Sie mit einigen Symbolen verfahren. attr_reader definiert den Getter, attr_writer definiert den Setter und attr_accessor definiert beides. Während Attribute nur Symbole annehmen, "deklarieren" sie keine Instanzvariablen, da es in Ruby keine solche Variable gibt. Es wird immer beim ersten Mal deklariert und initialisiert.

Also, nachdem du sagst, attr_accessor: some_hash, die Laufzeit kennt den Weg zum Zugriff auf eine Variable namens etwas_hash, aber diese Variable existiert nicht, bis Sie sie verwenden. Du kannst es versuchen:

Klasse Benutzer attr_accessor: some_hash attr_reader: some_other_hashEndeusr = Benutzer.neup usr.some_hashusr.some_hash = {}p usr.some_hashp usr.some_other_hashusr.some_other_hash = {}

Sie erhalten Nil, {}, Nil und ein Fehler. Der Fehler liegt daran, dass nur die Getter-Methode definiert ist, aber kein Setter. Wenn Sie einen Setter selbst definieren wollen, können Sie trotzdem eine Methode namens some_other_hash = ()

Mein Vorschlag für die Variable instance ist (was ich und vielleicht auch viele andere benutzen):

Klasse Benutzer attr_accessor: some_hash def initialisieren @some_hash = {} EndeEndeusr = Benutzer.neup usr.some_hash

Auf diese Weise wird eine "psudo" -Deklaration beim Erstellen eines Objekts für die Klasse und die Instanzvariable ebenfalls initialisiert.

antwortete am 21. März 12 um 18:50
texasbruce