Perché Ruby ha metodi sia privati ​​che protetti?


141

Prima di leggere questo articolo , pensavo che il controllo degli accessi in Ruby funzionasse in questo modo:

  • public- accessibile da qualsiasi oggetto (es. Obj.new.public_method)
  • protected - è possibile accedervi solo dall'interno dell'oggetto stesso, nonché da eventuali sottoclassi
  • private - uguale a protetto, ma il metodo non esiste nelle sottoclassi

Tuttavia, sembra che protectede privateagire lo stesso, tranne per il fatto che non si può chiamare privatemetodi con un ricevitore esplicito (cioè self.protected_methodopere, ma self.private_methodnon lo fa).

Che senso ha questo? Quando c'è uno scenario in cui non vorresti che il tuo metodo venisse chiamato con un ricevitore esplicito?


3
Se tutte le istanze di Objectfossero autorizzate a chiamare i metodi privati ​​di ogni altra istanza di Object, sarebbe possibile dire cose del genere 5.puts("hello world").
sepp2k,

Risposte:


161

protected i metodi possono essere chiamati da qualsiasi istanza della classe di definizione o delle sue sottoclassi.

privatei metodi possono essere chiamati solo dall'interno dell'oggetto chiamante. Non è possibile accedere direttamente ai metodi privati ​​di un'altra istanza.

Ecco un rapido esempio pratico:

def compare_to(x)
 self.some_method <=> x.some_method
end

some_methodnon può essere privatequi. Deve essere protectedperché ne hai bisogno per supportare ricevitori espliciti. I tuoi tipici metodi di supporto interno possono essere in genere privatepoiché non devono mai essere chiamati in questo modo.

È importante notare che questo è diverso dal modo in cui funziona Java o C ++. privatein Ruby è simile a protectedJava / C ++ in quanto le sottoclassi hanno accesso al metodo. In Ruby, non c'è modo di limitare l'accesso a un metodo dalle sue sottoclassi come puoi fare con privateJava.

La visibilità in Ruby è comunque in gran parte una "raccomandazione" poiché puoi sempre accedere a un metodo usando send:

irb(main):001:0> class A
irb(main):002:1>   private
irb(main):003:1>   def not_so_private_method
irb(main):004:2>     puts "Hello World"
irb(main):005:2>   end
irb(main):006:1> end
=> nil

irb(main):007:0> foo = A.new
=> #<A:0x31688f>

irb(main):009:0> foo.send :not_so_private_method
Hello World
=> nil

9
Ah, ok ha molto più senso. Il mio malinteso è venuto dal pensare che privatevs protecteddoveva fare se una sottoclasse potesse ereditare un metodo, ma in realtà riguarda da dove il metodo può essere chiamato. Grazie!
Kyle Slattery,

3
Anche i metodi privati ​​sono ignorati di default da RDoc quando si generano documentazione mentre quelli protetti non lo sono. Puoi sempre usare il flag --all per includerli.
jasoares,

Ma se lo vuoi davvero privato, non puoi ignorarlo send?
Cyoce,

78

La differenza

  • Chiunque può chiamare i tuoi metodi pubblici.
  • Puoi chiamare i tuoi metodi protetti, oppure un altro membro della tua classe (o una classe discendente) può chiamare i tuoi metodi protetti dall'esterno. Nessun altro può.
  • Solo tu puoi chiamare i tuoi metodi privati, perché possono essere chiamati solo con un destinatario implicito di self. Anche tu non puoi chiamare self.some_private_method; devi chiamare private_methodcon selfimplicito.
    • iGEL sottolinea: "Esiste tuttavia un'eccezione. Se hai un metodo privato age =, puoi (e devi) chiamarlo con sé per separarlo dalle variabili locali."
    • Dal momento che Ruby 2.7 il selfricevitore può essere esplicito, self.some_private_methodè consentito. (Qualsiasi altro ricevitore esplicito è ancora vietato, anche se il valore di runtime è lo stesso di self.)

In Ruby, queste distinzioni sono solo consigli da un programmatore all'altro. I metodi non pubblici sono un modo per dire "Mi riservo il diritto di cambiare questo, non dipendere da esso". Ma ottieni ancora le forbici affilate sende puoi chiamare qualsiasi metodo che ti piace.

Un breve tutorial

# dwarf.rb
class Dwarf
  include Comparable

  def initialize(name, age, beard_strength)
    @name           = name
    @age            = age
    @beard_strength = beard_strength
  end

  attr_reader :name, :age, :beard_strength
  public    :name
  private   :age
  protected :beard_strength

  # Comparable module will use this comparison method for >, <, ==, etc.
  def <=>(other_dwarf)
    # One dwarf is allowed to call this method on another
    beard_strength <=> other_dwarf.beard_strength
  end

  def greet
    "Lo, I am #{name}, and have mined these #{age} years.\
       My beard is #{beard_strength} strong!"
  end

  def blurt
    # Not allowed to do this: private methods can't have an explicit receiver
    "My age is #{self.age}!"
  end
end

require 'irb'; IRB.start

Quindi puoi eseguire ruby dwarf.rbe fare questo:

gloin = Dwarf.new('Gloin', 253, 7)
gimli = Dwarf.new('Gimli', 62,  9)

gloin > gimli         # false
gimli > gloin         # true

gimli.name            # 'Gimli'
gimli.age             # NoMethodError: private method `age'
                         called for #<Dwarf:0x007ff552140128>

gimli.beard_strength # NoMethodError: protected method `beard_strength'
                        called for #<Dwarf:0x007ff552140128>

gimli.greet          # "Lo, I am Gimli, and have mined these 62 years.\
                           My beard is 9 strong!"

gimli.blurt          # private method `age' called for #<Dwarf:0x007ff552140128>

8
Bella spiegazione! C'è comunque un'eccezione. Se hai un metodo privato age=, puoi (e devi) chiamarlo selfper separarlo dalle variabili locali.
iGEL

Se hai fatto "salutare" un metodo protetto, perché non puoi fare un gimli.greet? Dato che gimli è un membro della classe Dwarf, non dovrebbe essere in grado di chiamare questo metodo senza molestie?
JoeyC

@JoeyC perché quando lo fai gimli.greet, gimlinon è il chiamante, ma il destinatario. Il chiamante è "l'ambiente di esecuzione di livello superiore", che in realtà è un'istanza ad-hoc di Object. Prova questo:ruby -e 'p self; p self.class'
Kelvin,

52

Metodi privati ​​in Ruby:

Se un metodo è privato in Ruby, non può essere chiamato da un destinatario esplicito (oggetto). Può essere chiamato solo implicitamente. Può essere chiamato implicitamente dalla classe in cui è stato descritto, nonché dalle sottoclassi di questa classe.

I seguenti esempi lo illustreranno meglio:

1) Una classe Animal con metodo privato class_name

class Animal
  def intro_animal
    class_name
  end
  private
  def class_name
    "I am a #{self.class}"
  end
end

In questo caso:

n = Animal.new
n.intro_animal #=>I am a Animal
n.class_name #=>error: private method `class_name' called

2) Una sottoclasse di animali chiamata Anfibio:

class Amphibian < Animal
  def intro_amphibian
    class_name
  end 
end 

In questo caso:

  n= Amphibian.new
  n.intro_amphibian #=>I am a Amphibian
  n.class_name #=>error: private method `class_name' called

Come puoi vedere, i metodi privati ​​possono essere chiamati solo implicitamente. Non possono essere chiamati da destinatari espliciti. Per lo stesso motivo, i metodi privati ​​non possono essere chiamati al di fuori della gerarchia della classe di definizione.

Metodi protetti in Ruby:

Se un metodo è protetto in Ruby, può essere chiamato in modo implicito sia dalla classe che dalla sua definizione che dalle sue sottoclassi. Inoltre, possono anche essere chiamati da un destinatario esplicito a condizione che il destinatario sia self o della stessa classe di quello self:

1) Una classe di animali con metodo protetto protect_me

class Animal
  def animal_call
    protect_me
  end
  protected
  def protect_me
    p "protect_me called from #{self.class}"
  end  
end

In questo caso:

n= Animal.new
n.animal_call #=> protect_me called from Animal
n.protect_me #=>error: protected method `protect_me' called

2) Una classe di mammiferi ereditata da una classe animale

class Mammal < Animal
  def mammal_call
    protect_me
  end
end 

In questo caso

n= Mammal.new
n.mammal_call #=> protect_me called from Mammal

3) Una classe di anfibi ereditata dalla classe Animale (uguale alla classe dei mammiferi)

class Amphibian < Animal
  def amphi_call
    Mammal.new.protect_me #Receiver same as self
    self.protect_me  #Receiver is self
  end   
end

In questo caso

n= Amphibian.new
n.amphi_call #=> protect_me called from Mammal
             #=> protect_me called from Amphibian  

4) Una classe chiamata Albero

class Tree
  def tree_call
    Mammal.new.protect_me #Receiver is not same as self
  end
end

In questo caso:

n= Tree.new
n.tree_call #=>error: protected method `protect_me' called for #<Mammal:0x13410c0>

7

Prendi in considerazione un metodo privato in Java. Può essere chiamato dall'interno della stessa classe, ovviamente, ma può anche essere chiamato da un'altra istanza di quella stessa classe:

public class Foo {

   private void myPrivateMethod() {
     //stuff
   }

   private void anotherMethod() {
       myPrivateMethod(); //calls on self, no explicit receiver
       Foo foo = new Foo();
       foo.myPrivateMethod(); //this works
   }
}

Quindi - se il chiamante è un'istanza diversa della mia stessa classe - il mio metodo privato è effettivamente accessibile dall'esterno, per così dire. Questo in realtà sembra non sembrare così privato.

In Ruby, d'altra parte, un metodo privato è pensato per essere privato solo per l'istanza corrente. Questo è ciò che fornisce la rimozione dell'opzione di un ricevitore esplicito.

D'altra parte, dovrei certamente sottolineare che è abbastanza comune nella comunità di Ruby non usare affatto questi controlli di visibilità, dato che Ruby ti dà comunque modo di aggirarli. A differenza del mondo Java, la tendenza è rendere tutto accessibile e fidarsi degli altri sviluppatori per non rovinare tutto.


9
"è abbastanza comune nella comunità di Ruby non usare affatto questi controlli di visibilità" - questo può essere vero, ma direi che dovremmo usarli. Come le costanti, non sono manette, ma una comunicazione da un programmatore all'altro: "Ti consiglio di lasciarlo da solo". Puoi dipendere dai miei metodi pubblici; Posso modificare i miei metodi privati ​​senza preavviso, perché li considero dettagli di implementazione.
Nathan Long,

In Ruby, d'altra parte, un metodo privato è pensato per essere privato solo per l'istanza corrente. " Questo non è vero. Puoi comunque sovrascrivere accidentalmente metodi privati ​​dalla tua classe genitore (e alcune classi lo elencano persino come parte della loro API).
Franklin Yu,

1
@FranklinYu Questo non ha alcuna attinenza con ciò che ha scritto; la privacy in Ruby riguarda gli oggetti , non le classi , e riguarda i metodi di chiamata , non la loro definizione . Un metodo privato può essere chiamato solo da un altro metodo dello stesso oggetto; non ha nulla a che fare con la classe in cui è stato definito il metodo.
Filosofia

2

Parte del motivo per cui i sottoclassi in Ruby possono accedere ai metodi privati ​​è che l'ereditarietà di Ruby con le classi è una sottile copertura di zucchero rispetto al modulo. In Ruby, una classe, infatti, è una specie di modulo che fornisce eredità, ecc.

http://ruby-doc.org/core-2.0.0/Class.html

Ciò significa che sostanzialmente una sottoclasse "include" la classe genitore in modo tale che anche le funzioni della classe genitore, comprese le funzioni private , siano definite nella sottoclasse.

In altri linguaggi di programmazione, la chiamata di un metodo comporta il gorgogliamento del nome del metodo in una gerarchia di classi parent e la ricerca della prima classe parent che risponde al metodo. Al contrario, in Ruby, mentre la gerarchia della classe genitore è ancora lì, i metodi della classe genitore sono direttamente inclusi nell'elenco dei metodi della sottoclasse definita.


2

Confronto dei controlli di accesso di Java con Ruby: se il metodo è dichiarato privato in Java, è possibile accedervi solo con altri metodi all'interno della stessa classe. Se un metodo viene dichiarato protetto, è possibile accedervi da altre classi esistenti nello stesso pacchetto, nonché da sottoclassi della classe in un pacchetto diverso. Quando un metodo è pubblico è visibile a tutti. In Java, il concetto di visibilità del controllo di accesso dipende da dove si trovano queste classi nella gerarchia di ereditarietà / pacchetto.

Considerando che in Ruby, la gerarchia ereditaria o il pacchetto / modulo non si adattano. È tutto su quale oggetto è il destinatario di un metodo.

Per un metodo privato in Ruby, non può mai essere chiamato con un ricevitore esplicito. Possiamo (solo) chiamare il metodo privato con un ricevitore implicito.

Questo significa anche che possiamo chiamare un metodo privato all'interno di una classe in cui è dichiarato, così come tutte le sottoclassi di questa classe.

class Test1
  def main_method
    method_private
  end

  private
  def method_private
    puts "Inside methodPrivate for #{self.class}"
  end
end

class Test2 < Test1
  def main_method
    method_private
  end
end

Test1.new.main_method
Test2.new.main_method

Inside methodPrivate for Test1
Inside methodPrivate for Test2

class Test3 < Test1
  def main_method
    self.method_private #We were trying to call a private method with an explicit receiver and if called in the same class with self would fail.
  end
end

Test1.new.main_method
This will throw NoMethodError

Non è mai possibile chiamare il metodo privato dall'esterno della gerarchia di classi in cui è stato definito.

Il metodo protetto può essere chiamato con un ricevitore implicito, come privato. Inoltre, il metodo protetto può anche essere chiamato da un destinatario esplicito (solo) se il destinatario è "auto" o "un oggetto della stessa classe".

 class Test1
  def main_method
    method_protected
  end

  protected
  def method_protected
    puts "InSide method_protected for #{self.class}"
  end
end

class Test2 < Test1
  def main_method
    method_protected # called by implicit receiver
  end
end

class Test3 < Test1
  def main_method
    self.method_protected # called by explicit receiver "an object of the same class"
  end
end


InSide method_protected for Test1
InSide method_protected for Test2
InSide method_protected for Test3


class Test4 < Test1
  def main_method
    Test2.new.method_protected # "Test2.new is the same type of object as self"
  end
end

Test4.new.main_method

class Test5
  def main_method
    Test2.new.method_protected
  end
end

Test5.new.main_method
This would fail as object Test5 is not subclass of Test1
Consider Public methods with maximum visibility

Sommario

Pubblico: i metodi pubblici hanno la massima visibilità

Protetto: il metodo protetto può essere chiamato con un destinatario implicito, come privato. Inoltre, il metodo protetto può anche essere chiamato da un destinatario esplicito (solo) se il destinatario è "auto" o "un oggetto della stessa classe".

Privato: per un metodo privato in Ruby, non può mai essere chiamato con un ricevitore esplicito. Possiamo (solo) chiamare il metodo privato con un ricevitore implicito. Questo significa anche che possiamo chiamare un metodo privato all'interno di una classe in cui è dichiarato, così come tutte le sottoclassi di questa classe.


0
First Three types of access specifiers and those define thier scope.
1.Public    ->  Access anywhere out side the class.
2.Private   ->  Can not access outside the class. 
3.Protected ->  This Method not access anywhere this method define 
                scope.

But i have a solution for this problem for all method how to access explain in depth. 

class Test
attr_reader :name
def initialize(name)
  @name = name
end

def add_two(number)
  @number = number 
end

def view_address
  address("Anyaddress")
end

private 
def address(add)
   @add = add
end

protected 
def user_name(name)
  # p 'call method'
  @name = name
end
end

class Result < Test
def new_user
  user_name("test355")
end
end
  1. Elenco oggetti
  2. p test = Test.new ("test")
  3. p test.name
  4. p test.add_two (3)
  5. Voce di elenco
  6. p test.view_address
  7. pr = Result.new ("")
  8. p r.new_user

Alcuni problemi nella modifica del codice. Spettacolo di seconda classe in una riga precedente post precedente. Ora spiego come accedere a tutto il metodo. Prima creare la classe Test oggetto. Ma il metodo privato non può accedere alla classe esterna quindi accedere al metodo privato. creiamo l'accesso al metodo view_address tramite l'oggetto principale. e anche il metodo protetto accede alla creazione dell'ereditarietà.
Hardik,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.