Velkommen til en ny episode Av Ruby Magic! Denne månedens utgave handler om metaclasses, et emne utløst av en diskusjon mellom to utviklere (Hei Maud!).
gjennom å undersøke metaklasser lærer vi hvordan klasse-og forekomstmetoder fungerer I Ruby. Underveis oppdager du forskjellen mellom å definere en metode ved å sende en eksplisitt «definee» og bruke class << self
eller instance_eval
. Kom igjen!
Klasseforekomster Og Instansmetoder
for å forstå hvorfor metaklasser brukes I Ruby, begynner vi med å undersøke hva forskjellene er mellom instans-og klassemetoder.
I Ruby er en klasse et objekt som definerer en blåkopi for å opprette andre objekter. Klasser definerer hvilke metoder som er tilgjengelige på alle forekomster av den klassen.
Definere en metode i en klasse oppretter en instansmetode på den klassen. Enhver fremtidig forekomst av den klassen vil ha den metoden tilgjengelig.
1 2 3 4 5 6 7 8 9 10 11 12 |
class User def initialize(name) @name = name end def name @name end end user = User.new('Thijs') user.name # => "Thijs" |
i dette eksemplet oppretter vi en klasse kalt User
, med en forekomstmetode kalt #name
som returnerer brukerens navn. Ved hjelp av klassen oppretter vi en klasseforekomst og lagrer den i en variabel som heter user
. Siden user
er en forekomst av User
– klassen, har den #name
– metoden tilgjengelig.
en klasse lagrer forekomstmetodene i metodetabellen. Enhver forekomst av denne klassen refererer til klassens metodetabell for å få tilgang til forekomstmetodene.
Klasseobjekter
en klassemetode er en metode som kan kalles direkte på klassen uten å måtte opprette en forekomst først. En klassemetode opprettes ved å prefikse navnet med self.
når du definerer det.
en klasse er i seg selv et objekt. En konstant refererer til klasseobjektet, så klassemetoder definert på det kan kalles fra hvor som helst i applikasjonen.
1 2 3 4 5 6 7 8 9 |
class User # ... def self.all end end User.all # => |
Metoder definert med et self.
-prefiks legges ikke til i klassens metodetabell. De er i stedet lagt til klassens metaclass.
Metaclasses
bortsett fra en klasse har Hvert Objekt I Ruby en skjult metaclass. Metaklasser er enkeltfødte, noe som betyr at de tilhører et enkelt objekt. Hvis du oppretter flere forekomster av en klasse, deler de samme klasse, men de har alle separate metaklasser.
1 2 3 4 5 6 7 8 9 |
thijs, robert, tom = User.all thijs.class # => User robert.class # => User tom.class # => User thijs.singleton_class # => #<Class:#<User:0x00007fb71a9a2cb0>> robert.singleton_class # => #<Class:#<User:0x00007fb71a9a2c60>> tom.singleton_class # => #<Class:#<User:0x00007fb71a9a2c10>> |
I dette eksemplet ser vi at selv om hvert av objektene har klassen User
, har deres singleton-klasser forskjellige objekt-Ider, noe som betyr at de er separate objekter.
Ved å ha tilgang til en metaclass, Kan Ruby legge metoder direkte til eksisterende objekter. Dette vil ikke legge til en ny metode i objektets klasse.
1 2 3 4 5 6 7 8 |
robert = User.new("Robert") def robert.last_name "Beekman" end robert.last_name # => "Beekman" User.new("Tom").last_name # => NoMethodError (undefined method `last_name' for #<User:0x00007fe1cb116408>) |
i dette eksemplet legger vi til en #last_name
til brukeren som er lagret i robert
– variabelen. Selv om robert
er en forekomst av User
, vil eventuelle nyopprettede forekomster av User
ikke ha tilgang til #last_name
– metoden, da den bare finnes på robert
‘ s metaclass.
Hva er selv?
når du definerer en metode og passerer en mottaker, legges den nye metoden til mottakerens metaclass, i stedet for å legge den til klassens metodetabell.
1 2 3 4 5 |
tom = User.new("Tom") def tom.last_name "de Bruijn" end |
i eksemplet ovenfor har vi lagt til #last_name
direkte på tom
– objektet, ved å sende tom
som mottaker når du definerer metoden.
Slik fungerer det også for klassemetoder.
1 2 3 4 5 6 7 |
class User # ... def self.all end end |
Her passerer vi eksplisitt self
som mottaker når du oppretter .all
– metoden. I en klassedefinisjon refererer self
til klassen (User
i dette tilfellet), slik at .all
– metoden blir lagt til User
‘ s metaclass.
fordi User
er et objekt lagret i en konstant, får vi tilgang til det samme objektet—og samme metaclass-når vi refererer til det.
Åpning Av Metaclass
vi har lært at klassemetoder er metoder i klasseobjektets metaclass. Å vite dette, vil vi se på noen andre teknikker for å lage klassemetoder som du kanskje har sett før.
klasse << selv
selv om det har gått litt ut av stil, bruker noen biblioteker class << self
for å definere klassemetoder. Dette syntaxtricket åpner dagens klasses metaclass og samhandler med det direkte.
1 2 3 4 5 6 7 8 9 10 11 |
class User class << self self # => #<Class:User> def all end end end User.all # => |
dette eksemplet oppretter en klassemetode kalt User.all
ved å legge til en metode i User
‘ s metaclass. I stedet for eksplisitt å sende en mottaker for metoden som vi så tidligere, satte vi self
til User
‘ s metaclass i stedet for User
selv.
som vi lærte før, blir enhver metodedefinisjon uten en eksplisitt mottaker lagt til som en forekomstmetode for gjeldende klasse. Inne i blokken er den nåværende klassen User
‘ s metaclass (#<Class:User>
).
instance_eval
Et annet alternativ er å bruke instance_eval
, som gjør det samme med en stor forskjell. Selv om klassens metaclass mottar metodene definert i blokken, er self
fortsatt en referanse til hovedklassen.
1 2 3 4 5 6 7 8 9 10 11 |
class User instance_eval do self # => User def all end end end User.all # => |
i dette eksemplet definerer vi en forekomstmetode på User
‘ s metaclass akkurat som før, men self
peker fortsatt på User
. Selv om det vanligvis peker på det samme objektet, kan» default definee » og self
peke på forskjellige objekter.
Hva Vi Har Lært
vi har lært at klasser er de eneste objektene som kan ha metoder, og at forekomstmetoder faktisk er metoder på et objekts metaclass. Vi vet at class << self
bare bytter self
rundt for å tillate deg å definere metoder på metaclassen, og vi vet at instance_eval
gjør det meste det samme(men uten å berøre self
).
Selv om du ikke vil eksplisitt jobbe med metaclasses, Bruker Ruby dem mye under hetten. Å vite hva som skjer når du definerer en metode kan hjelpe deg å forstå hvorfor Ruby oppfører seg som det gjør(og hvorfor du må prefiks klasse metoder med self.
).
Takk for at du leste. Hvis du likte det du leser, kan du abonnere På Ruby Magic for å motta En e-post når vi publiserer en ny artikkel om en gang i måneden.