Ü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_eval
haszná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 user
nevű 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_name
metó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 .all
metó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 User
metaklassához. Ahelyett, hogy kifejezetten átadnánk a metódus vevőjét, amint azt korábban láttuk, a self
értéket User
metaclass-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 User
metaosztá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_eval
tö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é.