extend self in ruby

Az alábbi kódot ruby 1.9.3-mal teszteltük.

Az osztály az adatokra és a viselkedésre is vonatkozik

Nézzük meg ezt a ruby kódot.

1class Util2 def self.double(i)3 i*24 end5end67Util.double(4) #=> 8

Itt van egy Util osztály. De figyeljük meg, hogy ennek az osztálynak minden metódusa osztály metódus. Ennek az osztálynak nincsenek példányváltozói. Általában egy osztályt arra használnak, hogy adatokat és viselkedést is hordozzon, és ,ebben az esetben az Util osztály csak viselkedéssel rendelkezik és adatokkal nem.

Hasonló segédeszközök ruby-ban

Most, hogy egy kis rálátást kapjunk erre a vitára, nézzünk meg néhány ruby metódust, amelyek hasonló dolgot csinálnak. Íme néhány.

1require 'base64'2Base64.encode64('hello world') #=>"aGVsbG8gd29ybGQ=\n "34require 'benchmark'5Benchmark.measure { 10*2000 }67require 'fileutils'8FileUtils.chmod 0644, 'test.rb'910Math.sqrt(4) #=> 2

A fenti esetekben az osztály metódusa előbbi példány létrehozása nélkül kerül meghívásra. Ez tehát hasonló az általam használt Util.double .

Mégis nézzük meg, hogy mi az osztálya ezeknek az objektumoknak.

1Base64.class #=> Module2Benchmark.class #=> Module3FileUtils.class #=> Module4Math.class #=> Module

Ezek tehát nem osztályok, hanem modulok. Ez felveti a kérdést, hogy az okos srácok a ruby-core-nál miért implementálták őket modulokként, ahelyett, hogy egy osztályt hoztak volna létre, ahogy én tettem az Util esetében.

Az ok az, hogy a Class túl nehézkes ahhoz, hogy csak metódusokat hozzunk létre, mint double. Ahogy korábban megbeszéltük, egy osztálynak adatot és viselkedést is kell tartalmaznia. Ha csak a viselkedés érdekel, akkor a ruby azt javasolja, hogy modulként valósítsuk meg.

extend self is the answer

Mielőtt rátérnék a extend self tárgyalására, íme, hogyan fog kinézni a Util osztályom, miután a Class-ből Module-ba költöztem.

1module Util2 extend self34 def double(i)5 i * 26 end7end89puts Util.double(4) #=> 8

Szóval hogyan működik az extend self

Először is nézzük meg, mit csinál az extend.

1modul M2 def double(i)3 i * 24 end5end67class Calculator8 extend M9end10puts Calculator.double(4)

A fenti esetben Calculator kiterjeszti a M modult, és így a M modul összes példány metódusa közvetlenül elérhető Calculator számára.

Ez esetben Calculator egy osztály, amely kiterjesztette a M modult. Azonban Calculator nem kell, hogy osztály legyen egy modul kiterjesztéséhez.

Most próbáljunk meg egy olyan variációt, ahol Calculator egy modul.

1module M2 def double(i)3 i * 24 end5end67module Calculator8 extend M9end10puts Calculator.double(4) #=> 8

Itt a Calculator egy modul, amely egy másik modult bővít.

Most, hogy megértettük, hogy egy modul bővíthet egy másik modult, nézzük meg a fenti kódot, és kérdezzük meg, miért van egyáltalán szükség a M modulra. Miért nem tudjuk a double metódust közvetlenül a Calculator modulba áthelyezni. Próbáljuk ki.

1modul Calculator2 extend Calculator34 def double(i)5 i * 26 end7end8puts Calculator.double(4) #=> 8

Megszabadultam a M modultól, és a double metódust a Calculator modulba költöztettem. Mivel a M modul eltűnt, a extend M modulból átváltottam a extend Calculator-re.

Egy utolsó javítás.

A Calculator modulon belül mi a self. self az maga a Calculator modul. Tehát nincs szükség a Calculator kétszeri megismétlésére. Itt a végleges változat

1modul Calculator2 extend self34 def double(i)5 i * 26 end7end8puts Calculator.double(4) #=> 8

Az osztály átalakítása modullá

Minden alkalommal, amikor olyan kóddal találkoznék, mint a extend self, az agyam megáll egy pillanatra. Aztán rákeresnék a Google-ben. Utánaolvasok. Három hónappal később megismétlem az egész folyamatot.

A legjobb módja a tanulásnak, ha használod. Tehát elkezdtem keresni egy esetet, hogy használjam extend self. Nem jó gyakorlat kódot vadászni egy ötlet alkalmazásához, ami a fejedben van, de itt próbáltam tanulni.

Itt van egy korábbi pillanatkép a Util osztály metódusairól, amit egy projektben használtam.

1class Util2 def self.config2hash(file); end3 def self.in_cents(összeg); end4 def self.localhost2public_url(url, protocol); end5end

Az extend self kód használata után

1module Util2 extend self34 def config2hash(file); end5 def in_cents(összeg); end6 def localhost2public_url(url, protocol); end7end

Sokkal jobb lett. Világossá teszi a szándékot, és ,azt hiszem, összhangban van azzal, ahogy a ruby elvárná, hogy használjuk.

Egy másik használat összhangban van azzal, ahogy a Rails használja az extend self-et

Itt egy e-kereskedelmi alkalmazást építek, és minden új rendelésnek új rendelési számot kell kapnia egy harmadik fél értékesítési alkalmazásából. A kód így nézhet ki. A metódusok implementációját kihagytam, mert azok nem relevánsak a vita szempontjából.

1class Order2 def amount; end3 def buyer; end4 def shipped_at; end5 def number6 @number || self.class.next_order_number7 end89 def self.next_order_number; 'A100'; end10end111212puts Order.new.number #=> A100

Itt a módszer next_order_number egy másik értékesítési rendszer bonyolult hívását végezheti. Ideális esetben az Order osztálynak nem kellene exponálnia a next_order_number metódust . Tehát ezt a metódust private-nek tehetjük, de ez nem oldja meg az alapvető problémát. A probléma az, hogy a Order modellnek nem szabad tudnia, hogyan generálódik az új rendelésszám. Nos, áthelyezhetjük a next_order_number metódust egy másik Util osztályba, de ez túl nagy távolságot hozna létre.

Itt egy megoldás a extend self használatával.

1module Checkout2 extend self34 def next_order_number; 'A100'; end56 class Order7 def amount; end8 def buyer; end9 def shipped_at; end10 def number11 @number || Checkout.next_order_number12 end13 end14end1516puts Checkout::Order.new.number #=> A100

Sokkal jobb. Az Order osztály nem exponálja a next_order_number metódust, és ez a metódus ott van ugyanabban a fájlban. Nem kell megnyitni a Util osztályt.

Articles

Vélemény, hozzászólás?

Az e-mail-címet nem tesszük közzé.