Come creare un metodo di classe privata?


167

Come mai questo approccio di creare un metodo di classe privata funziona:

class Person 

    def self.get_name 
    persons_name 
    end 

    class << self 

    private 

    def persons_name 
     "Sam" 
    end 
    end 
end 

puts "Hey, " + Person.get_name 
puts "Hey, " + Person.persons_name #=> raises "private method `persons_name' called for Person:Class (NoMethodError)" 

ma questo non significa:

class Person 

    def self.get_name 
    persons_name 
    end 

    private 

    def self.persons_name 
    "Sam" 
    end 
end 

puts "Hey, " + Person.get_name 
puts "Hey, " + Person.persons_name 
+5

Ho appena visto questo articolo discutere modi per creare metodi di classe privata e ho pensato che era buono: http://jakeyesbeck.com/2016/01/24/ruby- -class-metodi privati ​​/?utm_source = rubyweekly & utm_medium = email 28 gen. 162016-01-28 22:07:26

206

private non sembra funzionare se si sta definendo un metodo su un oggetto esplicito (nel tuo caso self). Puoi usare private_class_method per definire i metodi di classe come privati ​​(o come hai descritto tu).

class Person 
    def self.get_name 
    persons_name 
    end 

    def self.persons_name 
    "Sam" 
    end 

    private_class_method :persons_name 
end 

puts "Hey, " + Person.get_name 
puts "Hey, " + Person.persons_name 

alternativa (in Ruby 2.1+), dal momento che una definizione di metodo restituisce un simbolo del nome del metodo, è anche possibile utilizzare questo come segue:

class Person 
    def self.get_name 
    persons_name 
    end 

    private_class_method def self.persons_name 
    "Sam" 
    end 
end 

puts "Hey, " + Person.get_name 
puts "Hey, " + Person.persons_name 

39

Di default tutti i metodi della classe sono pubblici. Per renderli privata che potrete usare come Module#private_class_method @tjwallace scritto o definire in modo diverso, come avete fatto:

class << self 

    private 

    def method_name 
    ... 
    end 
end 

class << self apre classe singleton di sé, in modo che i metodi possono essere ridefinite per l'oggetto sé corrente. Questo è usato per definire il metodo class/module ("statico"). Solo lì, la definizione di metodi privati ​​ti offre davvero metodi di classe privati.


94

ExiRe ha scritto:

Tale comportamento di rubino è davvero frustrante. Voglio dire se si sposta nella sezione privata self.method quindi NON è privato. Ma se lo lo sposti nella classe < <, allora improvvisamente funziona. È semplicemente disgustoso.

Confondere probabilmente lo è, frustrante potrebbe essere, ma non è assolutamente disgustoso.

ha perfettamente senso una volta capito modello a oggetti di Ruby e il corrispondente method lookup flow, soprattutto se si tiene conto che private è NON un modificatore di accesso/visibilità, ma in realtà una chiamata di metodo (con la classe come il suo destinatario) come discusso here ... non esiste una cosa come "una sezione privata" in Ruby.

Per definire privati ​​esempio metodi, si chiama private sulla classe dell'istanza per impostare la visibilità predefinita per i metodi definiti successivamente a privati ​​... e quindi ha perfettamente senso per definire private classe metodi chiamando private su la classe della classe, ie. il suo metaclasse.

Altro mainstream, linguaggi OO autoproclamati potrebbe dare una sintassi meno confusione, ma è sicuramente il commercio che fuori contro un (incoerente?) Modello oggetto confuso e meno consistente, senza la potenza degli impianti di metaprogrammazione di Ruby.

  0

Quindi, se ho capito bene, lo stesso ruby ​​non ha parole chiave modificatore di accesso (pubblico, privato e protetto), ma piuttosto ha metodi di modifica dell'accesso (pubblico, privato, protetto)? È questo qualcosa che dovrebbe essere messo in evidenza sul bug tracker di ruby ​​per Matz per implementare i modificatori di accesso alle parole chiave o è questo comportamento atteso? 21 mar. 132013-03-21 16:00:03

+10

@Edward È _designed_ in questo modo http://junichiito.blogspot.co.uk/2012/03/matz-answers-why-ruby-lets-sub-classes.html. Perché "corretto"? 03 apr. 132013-04-03 11:58:26

+1

In seguito a ciò, invece di eseguire 'private_class_method: method_name' si potrebbe fare' private_class_method def method_name ... '. 08 dic. 142014-12-08 23:32:08

  0

'send (private_method)' è accessibile anche all'esterno dell'oggetto. 23 giu. 152015-06-23 20:24:00

+1

@ bjt38 Solo per chiarezza, sarebbe 'private_class_method def self.method_name' 15 feb. 162016-02-15 09:52:14


4

Anche io, trovo Ruby (o almeno la mia conoscenza di esso) a corto del marchio in quest'area.Per esempio il seguente fa quello che voglio, ma è goffo,

class Frob 
    attr_reader :val1, :val2 

    Tolerance = 2 * Float::EPSILON 

    def initialize(val1, val2) 
     @val2 = val1 
     @val2 = val2 
     ... 
    end 

    # Stuff that's likely to change and I don't want part 
    # of a public API. Furthermore, the method is operating 
    # solely upon 'reference' and 'under_test' and will be flagged as having 
    # low cohesion by quality metrics unless made a class method. 
    def self.compare(reference, under_test) 
     # special floating point comparison 
     (reference - under_test).abs <= Tolerance 
    end 
    private_class_method :compare 

    def ==(arg) 
     self.class.send(:compare, val1, arg.val1) && 
     self.class.send(:compare, val2, arg.val2) && 
     ... 
    end 
end 

I miei problemi con il codice di cui sopra è che i requisiti di sintassi di Ruby e il mio codice metriche di qualità cospirano per fatta per il codice ingombrante. Per fare in modo che il codice funzioni come preferisco e per mettere a tacere le metriche, devo fare in modo che faccia confronti() un metodo di classe. Dal momento che non voglio che faccia parte della classe 'API pubblica, ho bisogno che sia privata, eppure' privata 'di non funziona. Invece sono costretto a usare "private_class_method" o alcuni di questi interventi. Questo, a sua volta, impone l'utilizzo di 'self.class.send (: confronta ...'. Per ogni variabile provo a '==()' Ora che è un po 'ingombrante

  0

Il fatto che sia necessario utilizzare __send__ non ha nulla a che fare con il" come "si contrassegnano i metodi di classe privati. I metodi privati ​​non possono essere chiamati dal "fuori". 22 apr. 152015-04-22 08:01:35


9

Solo per il. completezza, si può anche evitare di dichiarare private_class_method in una riga separata. io personalmente non piace questo uso, ma bene sapere che esiste.

private_class_method def self.method_name 
.... 
end 

1

metodi di istanza sono definiti all'interno di un blocco definizione di classe. I metodi di classe sono definiti come metodi singleton sulla classe singleton di una classe, anche informalmente noti come "metaclass" o "eigenclass". private non è una parola chiave, ma un metodo (Module#private).

Questa è una chiamata al metodo self#private/A#private che "alterna" accesso privato via per tutte le prossime definizioni dei metodi esempio fino attivata altrimenti:

class A 
    private 
    def instance_method_1; end 
    def instance_method_2; end 
    # .. and so forth 
end 

Come osservato in precedenza, metodi di classe sono metodi veramente singleton definite sulla classe singleton.

def A.class_method; end 

o utilizzando una sintassi speciale per aprire il corpo definizione della classe Singleton anonima di A:

class << A 
    def class_method; end 
end 

Il destinatario del "messaggio privato" - auto - all'interno class A è la oggetto di classe A. self all'interno del blocco class << A è un altro oggetto, la classe singleton.

L'esempio seguente in realtà chiama due diversi metodi chiamati private, utilizzando due diversi destinatari o destinazioni per la chiamata. Nella prima parte, definiamo un metodo di istanza privato ("sulla classe A"), nel secondo definiamo un metodo di classe privata (è in realtà un metodo singleton sull'oggetto classe singleton di A).

class A 
    # self is A and private call "A.private()" 
    private def instance_method; end 

    class << self 
    # self is A's singleton class and private call "A.singleton_class.private()" 
    private def class_method; end 
    end 
end 

Ora, riscrivere questo esempio un po ':

class A 
    private 
    def self.class_method; end 
end 

riesci a vedere l'errore [che i progettisti linguaggio Ruby] fatti? Si attiva l'accesso privato per tutti i metodi di istanza imminenti di A, ma si procede a dichiarare un metodo Singleton su una classe diversa, la classe Singleton.


-5

Come di Ruby 2.3.0

class Check 
    def self.first_method 
    second_method 
    end 

    private 
    def self.second_method 
    puts "well I executed" 
    end 
end 

Check.first_method 
#=> well I executed 
  0

Lo stavo provando con 'private def self.second_method' prima di ogni notazione di metodo, che non funzionava sul mio ruby ​​2.3.3. Ma questa notazione funziona per me. 03 giu. 172017-06-03 15:44:40

+9

Questo non è corretto, perché chiamare 'Check.second_method' funzionerebbe anche senza problemi, quindi non è privato. 25 lug. 172017-07-25 08:49:56