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

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

  • public – è ansible accedere a qualsiasi object (ad es. Obj.new.public_method )
  • protected – è ansible accedervi solo dall’interno dell’object stesso e da qualsiasi sottoclass
  • private – uguale a protetto, ma il metodo non esiste nelle sottoclassi

Tuttavia, sembra che private azioni protected e private le stesse, tranne per il fatto che non è ansible chiamare metodi private con un ricevitore esplicito (vale a dire self.protected_method works, ma self.private_method non lo fa).

Qual è il punto di questo? Quando c’è uno scenario in cui non vorresti che il tuo metodo venisse chiamato con un ricevitore esplicito?

protected metodi protected possono essere richiamati da qualsiasi istanza della class che definisce o delle sue sottoclassi.

private metodi private possono essere chiamati solo dall’object chiamante. Non è ansible 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_method non può essere private qui. Deve essere protected perché ne hai bisogno per supportare ricevitori espliciti. I tipici metodi di supporto interno di solito possono essere private poiché non è mai necessario chiamarli in questo modo.

È importante notare che questo è diverso dal modo in cui funziona Java o C ++. private in Ruby è simile a protected in Java / 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 è ansible con private in Java.

La visibilità in Ruby è in ogni caso 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 => # irb(main):009:0> foo.send :not_so_private_method Hello World => nil 

La differenza

  • Chiunque può chiamare i tuoi metodi pubblici.
  • Puoi chiamare i tuoi metodi protetti o un altro membro della tua class (o una class 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 ricevitore implicito di self . Anche tu non puoi chiamare self.some_private_method ; devi chiamare private_method con self implicito. (sottolinea GIGEL : “Esiste tuttavia un’eccezione: se si ha un’età metodo privata =, è ansible (e deve) chiamarla con sé per separarla dalle variabili locali.”)

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 questo”. Ma hai ancora le forbici affilate di send e 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.rb e 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 # gimli.beard_strength # NoMethodError: protected method `beard_strength' called for # gimli.greet # "Lo, I am Gimli, and have mined these 62 years.\ My beard is 9 strong!" gimli.blurt # private method `age' called for # 

Metodi privati ​​in Ruby:

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

I seguenti esempi lo illustreranno meglio:

1) Una class di animali con metodo privato nome_class

 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 sottoclass 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 class che definisce.

Metodi protetti in Ruby:

Se un metodo è protetto in Ruby, può essere chiamato implicitamente sia dalla class di definizione che dalle sue sottoclassi. Inoltre possono anche essere chiamati da un ricevitore esplicito fintanto che il ricevitore è auto o della stessa class di quello di sé:

1) Una class 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 class di mammifero che è ereditata dalla class 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 class di anfibi ereditata dalla class degli animali (uguale alla class 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 class 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 # 

Considera un metodo privato in Java. Può essere chiamato all’interno della stessa class, ovviamente, ma può anche essere chiamato da un’altra istanza di quella stessa class:

 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 class, il mio metodo privato è effettivamente accessibile dall’esterno, per così dire. Questo in effetti non sembra tutto così privato.

In Ruby, d’altra parte, un metodo privato è pensato per essere privato solo per l’istanza corrente. Questo è ciò che consente di rimuovere l’opzione di un ricevitore esplicito.

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

Confronto dei controlli di accesso di Java contro Ruby: se il metodo è dichiarato privato in Java, è ansible accedervi solo tramite altri metodi all’interno della stessa class. Se un metodo è dichiarato protetto, è ansible accedervi da altre classi presenti nello stesso pacchetto e sottoclassi della class in un pacchetto diverso. Quando un metodo è pubblico è visibile a tutti. In Java, il concetto di visibilità del controllo di accesso dipende da dove queste classi si trovano nella gerarchia ereditari / dei pacchetti.

Mentre in Ruby, la gerarchia dell’ereditarietà o il pacchetto / modulo non si adattano. È tutto su quale object è 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.

Ciò significa anche che possiamo chiamare un metodo privato all’interno di una class dichiarata e di tutte le sottoclassi di questa class.

 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 puoi mai 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 se fosse privato. Inoltre il metodo protetto può anche essere chiamato da un ricevitore esplicito (solo) se il ricevitore è "self" o "un object della stessa class".

  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 ricevitore implicito, come se fosse privato. Inoltre il metodo protetto può anche essere chiamato da un ricevitore esplicito (solo) se il ricevitore è "self" o "un object della stessa class".

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. Ciò significa anche che possiamo chiamare un metodo privato all'interno di una class dichiarata e di tutte le sottoclassi di questa class.

Parte del motivo per cui i sottoprogrammi in Ruby possono accedere ai metodi privati ​​è che l’ereditarietà di Ruby con le classi è un sottile rivestimento di zucchero su Module include – in Ruby, una class, infatti, è un tipo di modulo che fornisce ereditarietà, ecc.

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

Ciò significa che fondamentalmente una sottoclass “include” la class genitore in modo tale che le funzioni della class genitrice, incluse le funzioni private , siano definite anche nella sottoclass.

In altri linguaggi di programmazione, la chiamata di un metodo comporta il ribollire del nome del metodo su una gerarchia di classi genitore e la ricerca della prima class genitore che risponde al metodo. Al contrario, in Ruby, mentre la gerarchia della class genitore è ancora lì, i metodi della class genitrice sono direttamente inclusi nell’elenco dei metodi della sottoclass definiti.