extend self in ruby

Folgender Code wurde mit ruby 1.9.3 getestet.

Klasse ist sowohl für Daten als auch für Verhalten gedacht

Lassen Sie uns diesen Ruby-Code betrachten.

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

Hier haben wir eine Util Klasse. Aber beachten Sie, dass alle Methoden dieser Klasse Klassenmethoden sind. Diese Klasse hat keine Instanzvariablen. Normalerweise wird eine Klasse verwendet, um sowohl Daten als auch Verhalten zu transportieren, aber in diesem Fall hat die Util-Klasse nur Verhalten und keine Daten.

Ähnliche Hilfsprogramme in Ruby

Um nun eine Perspektive auf diese Diskussion zu bekommen, schauen wir uns einige Ruby-Methoden an, die ähnliche Dinge tun. Hier sind ein paar.

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

In allen obigen Fällen wird die Klassenmethode aufgerufen, ohne vorher eine Instanz zu erzeugen. Das ist also ähnlich wie bei Util.double.

Aber schauen wir mal, welche Klasse all diese Objekte haben.

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

Das sind also keine Klassen sondern Module. Das wirft die Frage auf, warum die schlauen Jungs von ruby-core sie als Module implementiert haben, anstatt eine Klasse zu erstellen, wie ich es für Util getan habe.

Der Grund ist, dass Class zu schwer ist, um nur Methoden wie double zu erstellen. Wie wir bereits besprochen haben, soll eine Klasse sowohl Daten als auch Verhalten haben. Wenn das einzige, was dich interessiert, das Verhalten ist, dann schlägt Ruby vor, es als Modul zu implementieren.

selbst erweitern ist die Antwort

Bevor ich mit der Diskussion von extend self fortfahre, ist hier, wie meine Util Klasse nach dem Wechsel von Class zu Module aussehen wird.

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

So wie funktioniert extend self

Zunächst wollen wir sehen, was extend macht.

1module M2 def double(i)3 i * 24 end5end67class Rechner8 extend M9end10puts Rechner.double(4)

Im obigen Fall erweitert Calculator das Modul M und somit sind alle Instanzmethoden des Moduls M direkt für Calculator verfügbar.

In diesem Fall ist Calculator eine Klasse, die das Modul M erweitert. Allerdings muss Calculator keine Klasse sein, um ein Modul zu erweitern.

Nun versuchen wir eine Variante, bei der Calculator ein Modul ist.

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

Hier ist Calculator ein Modul, das ein anderes Modul erweitert.

Nun, da wir verstehen, dass ein Modul ein anderes Modul erweitern kann, schauen wir uns den obigen Code an und fragen uns, warum das Modul M überhaupt benötigt wird. Warum können wir die Methode double nicht direkt in das Modul Calculator verschieben. Versuchen wir das mal.

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

Ich bin das Modul M losgeworden und habe die Methode double ins Modul Calculator verschoben. Da das Modul M weg ist, habe ich von extend M auf extend Calculator gewechselt.

Eine letzte Korrektur.

Innerhalb des Moduls Calculator was ist self. self ist das Modul Calculator selbst. Es ist also nicht nötig, Calculator zweimal zu wiederholen. Hier ist die endgültige Version

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

Eine Klasse in ein Modul umwandeln

Jedes Mal, wenn ich auf Code wie extend self stoße, hält mein Gehirn für einen Moment inne. Dann würde ich danach googeln. Werde darüber lesen. Drei Monate später wiederhole ich den ganzen Prozess.

Am besten lernt man es, wenn man es anwendet. Also habe ich angefangen, nach einem Fall zu suchen, den ich verwenden kann extend self. Es ist keine gute Praxis, nach Code zu jagen, um eine Idee anzuwenden, die man im Kopf hat, aber hier wollte ich lernen.

Hier ist ein Schnappschuss von Methoden der Klasse Util, die ich in einem Projekt verwendet habe.

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

Nachdem ich extend self Code verwendet habe, wurde

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

Viel besser. Es macht die Absicht klar und ich glaube, es ist im Einklang mit der Art und Weise, wie Ruby erwarten würde, dass wir es verwenden.

Eine weitere Verwendung im Einklang mit der Art und Weise, wie Rails extend self verwendet

Hier baue ich eine E-Commerce-Anwendung und jede neue Bestellung muss eine neue Bestellnummer von einer Drittanbieter-Verkaufsanwendung erhalten. Der Code könnte wie folgt aussehen. Ich habe die Implementierung der Methoden weggelassen, weil sie für diese Diskussion nicht relevant sind.

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'; end10end1112puts Order.new.number #=> A100

Hier könnte die Methode next_order_number einen komplizierten Aufruf an ein anderes Verkaufssystem machen. Idealerweise sollte die Klasse Order die Methode next_order_number nicht exponieren. Wir können diese Methode also zu private machen, aber das löst nicht das eigentliche Problem. Das Problem ist, dass das Modell Order nicht wissen sollte, wie die neue Auftragsnummer generiert wird. Nun, wir können die Methode next_order_number in eine andere Util Klasse verschieben, aber das würde zu viel Abstand schaffen.

Hier ist eine Lösung mit extend self.

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

Viel besser. Die Klasse Order stellt die Methode next_order_number nicht zur Verfügung und diese Methode befindet sich direkt in der gleichen Datei. Es ist nicht nötig, die Klasse Util zu öffnen.

Articles

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht.