Unraveling Klasser ,Forekomster Og Metaclasses I Ruby

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.



+