Unraveling osztályok, példányok és Metaclasses Ruby

Üdvözöljük a Ruby Magic új epizódjában! Az e havi kiadás a metaclasses-ről szól, amelyet két fejlesztő közötti vita váltott ki (Szia Maud!).

a metaclasses vizsgálatával megtanuljuk, hogyan működnek az osztály-és példány metódusok a Ruby-ban. Az út során fedezze fel a különbséget a metódus meghatározása között egy explicit “definee” átadásával és class << self vagy instance_evalhasználatával. Gyerünk!

Osztálypéldányok és példány metódusok

ahhoz, hogy megértsük, miért használnak metaklasszokat a Ruby – ban, először megvizsgáljuk, hogy mi a különbség a példány-és osztály metódusok között.

a Ruby-ban az osztály olyan objektum, amely meghatározza a tervrajzot más objektumok létrehozásához. Az osztályok meghatározzák, hogy mely módszerek állnak rendelkezésre az osztály bármely példányán.

metódus definiálása egy osztályon belül létrehoz egy példány metódust az adott osztályon. Az osztály bármely jövőbeli példányában elérhető lesz ez a módszer.

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" 

ebben a példában egy User nevű osztályt hozunk létre egy #name nevű példány metódussal, amely visszaadja a felhasználó nevét. Az osztály használatával létrehozunk egy osztálypéldányt, és egy usernevű változóban tároljuk. Mivel a user a User osztály egy példánya, rendelkezésre áll a #name módszer.

egy osztály a példány metódusait a metódus táblázatában tárolja. Az osztály bármely példánya az osztály metódustáblájára utal, hogy hozzáférjen a példány metódusaihoz.

Osztályobjektumok

az osztály metódus olyan metódus, amely közvetlenül meghívható az osztályon anélkül, hogy először létre kellene hoznia egy példányt. Egy osztály metódust úgy hozunk létre, hogy a nevét self. előtaggal adjuk meg annak meghatározásakor.

egy osztály maga is objektum. Az állandó az osztályobjektumra utal, így az azon definiált osztálymódszerek az alkalmazás bármely pontjáról meghívhatók.

1 2 3 4 5 6 7 8 9
class User # ... def self.all end end User.all # => 

A self.-előtaggal definiált módszerek nem kerülnek hozzáadásra az osztály metódustáblájához. Ehelyett hozzáadják őket az osztály metaclass-jához.

Metaclasses

eltekintve egy osztály, minden objektum Ruby egy rejtett metaclass. A metaklasszák szingulettek, vagyis egyetlen tárgyhoz tartoznak. Ha több példányt hoz létre egy osztályból, akkor ugyanaz az osztály lesz, de mindegyiknek külön metaosztálya lesz.

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>> 

ebben a példában azt látjuk, hogy bár mindegyik objektum osztály User, a szingulett osztályaik különböző objektumazonosítókkal rendelkeznek, vagyis különálló objektumok.

a Metaklasszhoz való hozzáféréssel a Ruby lehetővé teszi metódusok hozzáadását közvetlenül a meglévő objektumokhoz. Ezzel nem fog új metódust hozzáadni az objektum osztályához.

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>) 

ebben a példában hozzáadunk egy #last_name értéket a robert változóban tárolt felhasználóhoz. Bár a robert a User egy példánya, a User újonnan létrehozott példányai nem férnek hozzá a #last_namemetódushoz, mivel csak a robert metaklassán létezik.

mi az én?

egy metódus meghatározásakor és egy vevő átadásakor az új metódus hozzáadódik a vevő metaosztályához, ahelyett, hogy hozzáadná az osztály metódustáblájához.

1 2 3 4 5
tom = User.new("Tom") def tom.last_name "de Bruijn" end 

a fenti példában hozzáadtuk #last_name közvetlenül a tom objektumra, átadva tom mint vevő a módszer meghatározásakor.

ez is hogyan működik osztály módszerek.

1 2 3 4 5 6 7
class User # ... def self.all end end 

itt kifejezetten átadjuk a self – et vevőként a .all módszer létrehozásakor. Egy osztálydefinícióban a self az osztályra utal (ebben az esetbenUser), így az .allmetódus hozzáadódik a User metaosztályához.

mivel a User egy konstansban tárolt objektum, ugyanazt az objektumot—és ugyanazt a metaosztályt—fogjuk elérni, amikor hivatkozunk rá.

a Metaclass megnyitása

megtanultuk, hogy az osztály metódusok az osztály objektum metaclassának metódusai. Ennek ismeretében megvizsgáljuk az osztálymódszerek létrehozásának néhány más technikáját, amelyeket korábban láthatott.

class << self

bár ez egy kicsit kiment a divatból, néhány könyvtár az class << self – et használja az osztály metódusok meghatározásához. Ez a szintaxis trükk megnyitja az aktuális osztály metaklassát, és közvetlenül kölcsönhatásba lép vele.

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 # => 

ez a példa egy User.all nevű osztály metódust hoz létre egy metódus hozzáadásával a Usermetaklassához. Ahelyett, hogy kifejezetten átadnánk a metódus vevőjét, amint azt korábban láttuk, a self értéket Usermetaclass-ra állítottuk a User helyett.

amint azt korábban megtanultuk, minden explicit vevő nélküli metódusdefiníció hozzáadódik az aktuális osztály példány metódusaként. A blokk belsejében az aktuális osztály User metaclass (#<Class:User>).

instance_eval

egy másik lehetőség a instance_eval használata, amely ugyanazt teszi egy fő különbséggel. Bár az osztály metaosztálya megkapja a blokkban meghatározott metódusokat, a self továbbra is a Főosztályra utal.

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 # => 

ebben a példában a Usermetaosztályán definiálunk egy példány metódust, mint korábban, de a self továbbra is a User – re mutat. Bár általában ugyanarra az objektumra mutat, a “default definee” és a self különböző objektumokra mutathat.

amit megtanultunk

megtanultuk, hogy az osztályok az egyetlen objektumok, amelyeknek metódusai lehetnek, és hogy a példány metódusok valójában metódusok egy objektum metaosztályán. Tudjuk, hogy az class << self egyszerűen kicseréli a self – et, hogy lehetővé tegye a metaosztály metódusainak meghatározását, és tudjuk, hogy az instance_evaltöbbnyire ugyanazt teszi (de a self megérintése nélkül).

bár nem fog kifejezetten dolgozni metaclasses, Ruby használja őket széles körben a motorháztető alatt. Annak ismerete, hogy mi történik egy metódus definiálásakor, segíthet megérteni, hogy a Ruby miért viselkedik így (és miért kell előtagot adni az osztály metódusoknak a self.értékkel).

köszönöm az olvasást. Ha tetszett, amit olvastál, érdemes feliratkozni a Ruby Magic-re, hogy e-mailt kapjon, amikor havonta egyszer új cikket teszünk közzé.



+