Ruby privat klass metoder

24 Jan 2016

i Ruby-programmeringsspråket finns definierade metoder i två varianter: instansmetoder och klassmetoder. Instansmetoder är tillgängliga efter att ett objekt har initierats och skapat en instans. Klassmetoder är å andra sidan tillgängliga utan att skapa en instans av den klass de definieras på.

Ruby metoder kan variera i synlighet. Offentliga metoder är tillgängliga i alla sammanhang,medan privata metoders tillgänglighet är begränsad inom en klass och dess ättlingar. Ett tredje synlighetsomfång, skyddat, beter sig på samma sätt som privata metoder, men skyddade metoder kan anropas av andra instanser av samma klass.

för en snabb uppdatering ser offentliga och privata instansmetoder ut så här:

class Dog def do_trick bark end private def bark puts 'woof woof' endend

när den offentliga metoden kallas:

> dog = Dog.new> dog.do_trick# => woof woof

och den privata metoden:

> dog = Dog.new> dog.bark# => NoMethodError: private method `bark' called for <Dog>

privata klassmetoder kanske inte är lika vanliga som privata instansmetoder, men de har fortfarande sin plats. Till exempel kan en klassmetod kräva interna hjälpmetoder för att slutföra sin funktion. Oavsett anledning har definitionen av privata klassmetoder värde men är inte alltid intuitiv.

detta exempel Dog klass måste upprätthålla en lista med tricks som kommer att användas inom andra offentliga klassmetoder. Denna lista bör inte vara tillgänglig för alla som ringer utanför klassen Dog.

fel väg

ett första pass vid skrivning av den privata tricks – metoden kan se ut:

class Dog private def self.tricks endend

men när man testar synligheten för tricks – metoden:

> Dog.tricks# => 

Uh oh, inget fel kastades indikerar en att en privat metod kallades, denna metod är helt Offentlig.

varför?

anledningen till att ovanstående kod inte producerade en privat metod har att göra med Rubys objekthierarki, interaktioner mellan interna klasser, instanser av dessa klasser och egenklasser. En detaljerad skrivning om hur Rubys objekt fungerar med varandra finns i ett tidigare inlägg.

när metoder definieras i en klass skiljer sig standardkontexten och det sammanhang som introduceras av metoddeklarationen self..

det privata metodomfånget kan inte användas på ovanstående sätt eftersom det inte hanterar kontextförändringen mellan metoder definierade på klassen Dog och definierade inom self sammanhang.

så vad är alternativen?

klass<< själv

ett alternativt sätt att definiera klassmetoder på ett objekt är att använda syntaxen class << self. Denna syntax öppnar en egenklass för det medföljande argumentet. I det här exemplet öppnar self inom Dog klassen Dog's egenklass.

class Dog class << self private def tricks end endend

en sak att notera är att när man definierar metoder som detta har deklarationen ändrats från def self.tricks till helt enkelt def tricks.

nu bevaras den privata räckvidden och förväntat beteende uppnås:

> Dog.tricks# => NoMethodError: private method `tricks' called for Dog:Class

moduler

moduler i ruby är samlingar av metoder och konstanter. Dessa samlingar kan användas som inkapslingsverktyg eller i detta fall alternativ till att definiera offentliga och privata klassmetoder.

när en klass extends en modul blir alla metoder inom den modulen klassmetoder på ämnesklassen*. Detta mönster kan användas för att definiera tricks – metoden på Dog.

* notera: Detta gäller endast metoder som definieras i en ”typisk mening” Alla def self. eller def Dog. metoddefinitioner i en modul blir inte automatiskt klassmetoder på samma sätt när extended.

class Dog module ClassMethods private def tricks end end extend ClassMethodsend> Dog.tricks# => NoMethodError: private method `tricks' called for Dog:Class

en fördel med detta tillvägagångssätt är läsbarhet. Modulen med namnet ClassMethods, som ingår och därmed inkapslas av klassen Dog, är det tydliga hemmet för alla önskade klassmetoder.

Synlighetsmodifierare som private beter sig i enlighet därmed och en enkel extend längst ner i moduldefinitionen sammanför allt.

private_class_method

ett tredje tillvägagångssätt är att använda den inbyggda metoden Module#private_class_method. Syftet med denna metod är att ändra synligheten för befintliga klassmetoder.

till skillnad från de andra två lösningarna kräver den här inte en annan typ av metoddefinition än den felaktiga lösningen:

class Dog def self.tricks end private_class_method :tricksend> Dog.tricks# => NoMethodError: private method `tricks' called for Dog:Class

denna metod kan också vara in-fodrad under klassens metoddefinition:

class Dog private_class_method def self.tricks endend> Dog.tricks# => NoMethodError: private method `tricks' called for Dog:Class

om en enda klassmetod måste vara privat och spara linjer är mycket viktigt kan den här stilen vara tillämplig; men det offrar verkligen en nivå av läsbarhet.

en stor styrka av private_class_method är dess explicitness. Till skillnad från de andra metoderna kräver den här inte en speciell moduldefinition eller metoddefinitionskontextbyte.

Vidare läsning

de ovannämnda lösningarna kommer att få jobbet gjort men kanske inte svara på långvariga frågor om varför saker fungerar som de gör. Några bra artiklar om ruby eigenclass och Matzs tankar om Ruby method design bör hjälpa till att måla en mer kortfattad bild av varför Rubys privata klassmetoddefinition är komplex.

You might also like

Lämna ett svar

Din e-postadress kommer inte publiceras.