24 Jan 2016
I Programmeringsspråket Ruby kommer definerte metoder i to varianter: instansmetoder og klassemetoder. Instansmetoder er tilgjengelige etter at et objekt er initialisert, og oppretter en forekomst. Klassemetoder, derimot, er tilgjengelige uten å opprette en forekomst av klassen de er definert på.
Ruby-metoder kan variere i synlighet. Offentlige metoder er tilgjengelige i enhver sammenheng, mens private metoder er begrenset i forekomsten av en klasse og dens etterkommere. Et tredje synlighetsområde, beskyttet, oppfører seg på samme måte som private metoder, men beskyttede metoder kan kalles av andre forekomster av samme klasse.
for en rask oppfriskning ser offentlige og private instansmetoder slik ut:
class Dog def do_trick bark end private def bark puts 'woof woof' endend
når den offentlige metoden kalles:
> dog = Dog.new> dog.do_trick# => woof woof
den private metoden:
> dog = Dog.new> dog.bark# => NoMethodError: private method `bark' called for <Dog>
Private klassemetoder er kanskje ikke like vanlige som private instansmetoder, men de har fortsatt sin plass. For eksempel kan en klassemetode kreve interne hjelpemetoder for å fullføre sin funksjon. Uansett årsak, definere private klasse metoder har verdi, men er ikke alltid intuitivt.
dette eksemplet Dog
klassen må opprettholde en liste over tricks
som skal brukes i de andre metodene for offentlig klasse. Denne listen skal ikke være tilgjengelig for innringere utenfor klassen Dog
.
feil vei
et første pass ved å skrive privat tricks
– metoden kan se ut:
class Dog private def self.tricks endend
Men når du tester synligheten til tricks
– metoden:
> Dog.tricks# =>
Uh oh, ingen feil ble kastet som indikerer at en privat metode ble kalt, denne metoden er helt offentlig.
Hvorfor?
grunnen til at koden ovenfor ikke produserte en privat metode, har Å gjøre med Rubys objekthierarki, interaksjoner mellom interne klasser, forekomster av disse klassene og eigenclasses. En detaljert skrive opp om Hvordan Ruby objekter arbeide med hverandre kan bli funnet i et tidligere innlegg.
når du definerer metoder i en klasse, er standardkonteksten og konteksten introdusert av metodedeklarasjonen self.
distinkte.
det private metodeområdet kan ikke brukes på ovennevnte måte, da det ikke håndterer kontekstendringen mellom metoder definert i Dog
– klassen og definert i self
– konteksten.
så hva er alternativene?
klasse < < selv
en alternativ måte å definere klassemetoder på et objekt er å bruke syntaksen class << self
. Denne syntaksen åpner en egenklasse for det medfølgende argumentet. I dette eksemplet vil self
i Dog
– klassen åpne Dog's
egenklassen.
class Dog class << self private def tricks end endend
En ting å merke seg er at når man definerer metoder som dette, har erklæringen endret seg fra def self.tricks
til bare def tricks
.
nå bevares det private omfanget og forventet oppførsel oppnås:
> Dog.tricks# => NoMethodError: private method `tricks' called for Dog:Class
Moduler
Moduler I ruby er samlinger av metoder og konstanter. Disse samlingene kan brukes som innkapslingsverktøy eller i dette tilfellet alternativer til å definere offentlige og private klassemetoder.
når en klasse extends
en modul, blir alle metodene i den modulen klassemetoder på emneklassen*. Dette mønsteret kan brukes til å definere tricks
– metoden på Dog
.
* Merknad: Dette gjelder bare metoder definert i en «typisk forstand» noen def self.
eller def Dog.
metodedefinisjoner i en modul blir ikke automatisk klassemetoder på samme måte 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 fordel med denne tilnærmingen er lesbarhet. Modulen som heter ClassMethods
, som er inneholdt og dermed innkapslet av Dog
– klassen, er det klare hjemmet for alle ønskede klassemetoder.
Synlighetsmodifikatorer som private
oppfører seg tilsvarende, og en enkel extend
nederst i moduldefinisjonen bringer alt sammen.
private_class_method
en tredje tilnærming er å bruke den innebygde metoden Module#private_class_method
. Formålet med denne metoden er å endre synligheten til eksisterende klassemetoder.
I Motsetning til de to andre løsningene, krever denne ikke en annen metode for definisjon fra feil løsning:
class Dog def self.tricks end private_class_method :tricksend> Dog.tricks# => NoMethodError: private method `tricks' called for Dog:Class
denne metoden kan også være in-lined under klassens metodedefinisjon:
class Dog private_class_method def self.tricks endend> Dog.tricks# => NoMethodError: private method `tricks' called for Dog:Class
Hvis en enkelt klassemetode må være privat og lagre linjer er svært viktig, kan denne stilen være aktuelt; men det ofrer absolutt et nivå av lesbarhet.
en stor styrke på private_class_method
er dens eksplisitt. I motsetning til de andre tilnærmingene krever denne ikke en spesiell moduldefinisjon eller metodedefinisjons kontekstsvitsjing.
Videre Lesing
de nevnte løsningene vil få jobben gjort,men kan ikke svare på dvelende spørsmål om hvorfor ting fungerer som de gjør. Noen gode artikler om ruby eigenclass og Matz tanker Om Ruby metode design bør bidra til å male et mer konsist bilde av hvorfor Ruby private class metode definisjon er kompleks.