Utilizzo di attributi con codice rubino

Andreas Larsson / Folio Images / Getty Images

Guarda qualsiasi codice orientato agli oggetti e tutto segue più o meno lo stesso schema. Crea un oggetto, chiama alcuni metodi su quell'oggetto e accedi agli attributi di quell'oggetto. Non c'è molto altro che puoi fare con un oggetto se non passarlo come parametro al metodo di un altro oggetto. Ma ciò di cui ci occupiamo qui sono attributi.

Gli attributi sono come variabili di istanza a cui è possibile accedere tramite la notazione a punti dell'oggetto. Per esempio, person.name avrebbe accesso al nome di una persona. Allo stesso modo, puoi spesso assegnare attributi come person.name = "Alice". Questa è una caratteristica simile alle variabili membro (come in C ++), ma non è la stessa cosa. Non c'è niente di speciale qui, gli attributi sono implementati nella maggior parte delle lingue usando "getter" e "setter" o metodi che recuperano e impostano gli attributi dalle variabili di istanza.

Ruby non fa distinzione tra getter e setter degli attributi e metodi normali. A causa del metodo flessibile di Ruby che chiama sintassi, non è necessario fare alcuna distinzione. Per esempio, person.name e person.name () sono la stessa cosa, stai chiamando il nome metodo con parametri zero. Si assomiglia ad una chiamata al metodo e l'altra sembra un attributo, ma in realtà sono entrambe la stessa cosa. Entrambi stanno solo chiamando il nome metodo. Allo stesso modo, qualsiasi nome di metodo che termina con un segno di uguale (=) può essere utilizzato in un compito. La dichiarazione person.name = "Alice" è davvero la stessa cosa di person.name = (Alice), anche se c'è uno spazio tra il nome dell'attributo e il segno di uguale, è ancora solo chiamando il name = metodo.

Implementazione di attributi da soli

Puoi facilmente implementare gli attributi da solo. Definendo i metodi setter e getter, puoi implementare qualsiasi attributo che desideri. Ecco alcuni esempi di codice che implementano il nome attributo per una classe di persona. Memorizza il nome in a @nome variabile di istanza, ma il nome non deve essere lo stesso. Ricorda, non c'è nulla di speciale in questi metodi.

 #! / usr / bin / env ruby ​​class Person def initialize (nome) @name = nome end def name @name end def name = (nome) @name = nome end def say_hello inserisce "Hello, # {@ name}" end fine 

Una cosa che noterai subito è che è molto lavoro. È un sacco di digitazione solo per dire che vuoi un attributo chiamato nome che accede al @nome variabile di istanza. Fortunatamente, Ruby fornisce alcuni metodi di convenienza che definiranno questi metodi per te.

Usando attr_reader, attr_writer e attr_accessor

Ci sono tre metodi nel Modulo classe che è possibile utilizzare all'interno delle dichiarazioni di classe. Ricorda che Ruby non fa alcuna distinzione tra runtime e "tempo di compilazione" e qualsiasi codice all'interno delle dichiarazioni di classe non può solo definire metodi ma anche metodi di chiamata. Chiamando il attr_reader, attr_writer e attr_accessor i metodi definiranno a loro volta i setter e i getter che ci stavamo definendo nella sezione precedente.

Il attr_reader il metodo fa proprio come sembra. Prende un numero qualsiasi di parametri simbolo e, per ciascun parametro, definisce un metodo "getter" che restituisce la variabile di istanza con lo stesso nome. Quindi, possiamo sostituire il nostro nome metodo nell'esempio precedente con attr_reader: nome.

Allo stesso modo, il attr_writer metodo definisce un metodo "setter" per ogni simbolo passato ad esso. Si noti che il segno di uguale non deve essere parte del simbolo, ma solo il nome dell'attributo. Possiamo sostituire il name = metodo dell'esempio precedente con una chiamata a attr_writier: nome.

E, come previsto, attr_accessor fa il lavoro di entrambi attr_writer e attr_reader. Se è necessario sia un setter che un getter per un attributo, è prassi comune non chiamare separatamente i due metodi e chiamare invece attr_accessor. Potremmo sostituire entrambi il nome e name = metodi dell'esempio precedente con una singola chiamata a attr_accessor: nome.

 #! / usr / bin / env ruby ​​def person attr_accessor: nome def initialize (nome) @name = nome end def say_hello inserisce "Hello, # {@ name}" end end 

Perché definire manualmente setter e getter?

Perché dovresti definire i setter manualmente? Perché non usare il attr_ * metodi ogni volta? Perché rompono l'incapsulamento. L'incapsulamento è il principio che afferma che nessuna entità esterna dovrebbe avere accesso illimitato allo stato interno dei propri oggetti. È necessario accedere a tutto utilizzando un'interfaccia che impedisca all'utente di danneggiare lo stato interno dell'oggetto. Usando i metodi di cui sopra, abbiamo creato un grosso buco nel nostro muro di incapsulamento e permesso assolutamente qualsiasi cosa da impostare per un nome, anche se ovviamente nomi non validi.

Una cosa che vedrai spesso è quella attr_reader verrà utilizzato per definire rapidamente un getter, ma verrà definito un setter personalizzato poiché lo stato interno dell'oggetto spesso lo desidera leggere direttamente dallo stato interno. Il setter viene quindi definito manualmente e controlla per assicurarsi che il valore impostato sia sensato. O, forse più comunemente, nessun setter è definito affatto. Gli altri metodi nella funzione class impostano la variabile di istanza dietro al getter in qualche altro modo.

Ora possiamo aggiungere un età e implementare correttamente a nome attributo. Il età l'attributo può essere impostato nel metodo del costruttore, letto usando il età getter ma solo manipolato usando il have_birthday metodo, che aumenterà l'età. Il nome l'attributo ha un getter normale, ma il setter si assicura che il nome sia in maiuscolo ed abbia la forma di Nome e cognome.

 #! / usr / bin / env ruby ​​class Person def initialize (nome, età) self.name = nome @age = age end attr_reader: nome,: age def name = (new_name) se new_name = ~ / ^ [AZ] [ az] + [AZ] [az] + $ / @name = new_name else puts "'# {new_name}' non è un nome valido!" end end def have_birthday mette "Happy birthday # {@ name}!" @age + = 1 end def whoami inserisce "You are # {@ name}, age # {@ age}" end end p = Person.new ("Alice Smith", 23) # Chi sono io? p.whoami # Si è sposata p.name = "Alice Brown" # Ha cercato di diventare un eccentrico musicista p.name = "A" # Ma fallito # È diventata un po 'più grande p.have_birthday # Chi sono io di nuovo? p.whoami 

Utilizzo di attributi con codice rubino

Conteggio voti: 1

Così creo una classe nel rubino:

utente di classe def inizializzare finefine

Ora dire che voglio creare un attributo che è un hash con un getter / setter, sono confuso per quanto riguarda le opzioni che ho fatto.

Se faccio questo:

utente di classe attr_accessor: some_hashfine

Ma non voglio che questo hash sia mai nullo, sempre un hash vuoto.

Sono confuso quando dovrei fare:

def some_hash self.some_hash || = {}fine

e fai:

def some_hash @some_hash || = {}fine

Qual è la differenza?

Se non utilizzo l'attr_accessor, devo creare sia il lettore che lo scrittore (o getter / setter):

def some_hash = ()finedef some_hashfine

Spero che qualcuno possa chiarire le opzioni che avrei per creare un attributo some_hash che è un hash e che non restituisce mai nulla se è vuoto.

cioè usando attr_accessor, creando manualmente i metodi, e infine quando usare @some_hash e self.some_hash

chiesto il 21 marzo 12 alle 17:33
Blankman

2 risposte

Conteggio voti: 8 accettato

attr_accessor: some_hash definisce i metodi reader e writer per l'attributo specificato. È equivalente a questo:

utente di classe def some_hash @some_hash fine def some_hash = (some_hash) @some_hash = some_hash finefine

@some_hash si riferisce a una variabile di istanza dell'oggetto, mentre una_hash e una_hash = siamo metodi. Il primo restituisce il valore della variabile, quest'ultimo lo imposta.

L'idioma self.some_hash || = {} è equivalente a self.some_hash || self.some_hash = {}.

Operatori booleani in corto circuito Ruby, che significa la seconda espressione (self.some_hash = {}) non verrà eseguito affatto se la prima espressione (self.some_hash) restituisce un valore vero.

Il metodo:

def some_hash self.some_hash || = {}fine

In realtà è ricorsivo, dal momento che si espande some_hash || self.some_hash = {}. Continuerà a chiamarsi fino a ottenere un overflow dello stack. Usa il secondo modulo:

def some_hash @some_hash || = {}fine

Poiché imposta direttamente la variabile di istanza, non avrai problemi con la ricorsione, né dovrai chiamare un metodo di scrittura. una_hash non potrò mai più tornare zero, perchè se @some_hash è zero, verrà assegnato un hash vuoto prima che il metodo ritorni.

A proposito, questo è anche chiamato inizializzazione pigra, perché la variabile viene inizializzata quando viene prima consultata, non quando il Utente l'istanza è creata.

modificato il 21 marzo 12 alle 18:15
rispose il 21 marzo 12 alle 17:43
Matheus Moreira

Conteggio voti: 4

A parte la risposta di Matheus, un po 'di più sugli attributi:

Gli attributi sono metodi. Definiscono come si fa con alcuni simboli. attr_reader definisce il getter, attr_writer definisce il setter e attr_accessor definisce entrambi. Mentre gli attributi accettano solo simboli, non "dichiarano" alcuna variabile di istanza, poiché non esiste una cosa simile in Ruby come dichiarazione di una variabile. Viene sempre dichiarato e inizializzato quando viene utilizzato per la prima volta.

Quindi dopo aver detto attr_accessor: some_hash, il runtime conoscerà il modo di accedere a una variabile chiamata una_hash, ma questa variabile non esiste finché non la usi. Puoi provare:

utente di classe attr_accessor: some_hash attr_reader: some_other_hashfineusr = User.newp usr.some_hashusr.some_hash = {}p usr.some_hashp usr.some_other_hashusr.some_other_hash = {}

Otterrete nil, {}, nil e un errore. L'errore è perché è definito solo il metodo getter ma nessun setter. Se vuoi definire tu stesso un setter, puoi comunque scrivere un metodo chiamato some_other_hash = ()

Il mio suggerimento per la variabile di esempio è (che io e forse molti altri usano):

utente di classe attr_accessor: some_hash def inizializzare @some_hash = {} finefineusr = User.newp usr.some_hash

In questo modo viene eseguita una dichiarazione "psudo" quando si crea un oggetto per la classe e anche la variabile di istanza verrà inizializzata.

rispose il 21 marzo 12 alle 18:50
texasbruce
4.1
5
13
4
4
3
2
2
1
1
1