ruby language-design access-specifier

¿Por qué Ruby tiene métodos privados y protegidos?



language-design access-specifier (6)

Antes de leer este artículo , pensé que el control de acceso en Ruby funcionaba así:

  • public - se puede acceder por cualquier objeto (por ejemplo, Obj.new.public_method )
  • protected : solo se puede acceder desde el propio objeto, así como a cualquier subclase
  • private : igual que protected, pero el método no existe en subclases

Sin embargo, parece que private acto protected y el private actúan de la misma manera, excepto por el hecho de que no puede llamar a métodos private con un receptor explícito (es decir, el self.protected_method funciona, pero self.private_method no lo hace).

¿Cuál es el punto de esto? ¿Cuándo hay un escenario en el que no desea que su método se llame con un receptor explícito?


La diferencia

  • Cualquiera puede llamar a tus métodos públicos.
  • Puede llamar a sus métodos protegidos, u otro miembro de su clase (o una clase descendiente) puede llamar a sus métodos protegidos desde el exterior. Nadie más puede.
  • Solo usted puede llamar a sus métodos privados, porque solo pueden invocarse con un receptor de self implícito. Incluso usted no puede llamar a self.some_private_method ; debe llamar a private_method con private_method self . ( iGEL señala: "Sin embargo, hay una excepción. Si tiene un método privado age =, puede (y debe) llamarlo consigo mismo para separarlo de las variables locales").

En Ruby, estas distinciones son solo consejos de un programador a otro. Los métodos no públicos son una forma de decir "me reservo el derecho de cambiar esto, no dependo de él". Pero aún obtienes las tijeras afiladas de send y puedes llamar a cualquier método que desees.

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

Entonces puedes ejecutar ruby dwarf.rb y hacer esto:

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>


Métodos privados en Ruby:

Si un método es privado en Ruby, no puede ser llamado por un receptor explícito (objeto). Solo puede ser invocado implícitamente. Puede llamarse implícitamente por la clase en la que se ha descrito así como por las subclases de esta clase.

Los siguientes ejemplos lo ilustrarán mejor:

1) Una clase de Animal con método privado class_name

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

En este caso:

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

2) Una subclase de Animal llamada Anfibio:

class Amphibian < Animal def intro_amphibian class_name end end

En este caso:

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

Como puede ver, los métodos privados solo se pueden llamar implícitamente. No pueden ser llamados por receptores explícitos. Por la misma razón, los métodos privados no se pueden llamar fuera de la jerarquía de la clase de definición.

Métodos protegidos en Ruby:

Si un método está protegido en Ruby, puede llamarlo implícitamente tanto la clase definitoria como sus subclases. Además, también pueden ser llamados por un receptor explícito, siempre que el receptor sea uno mismo o de la misma clase que el propio:

1) Una clase de Animal con método protegido protect_me

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

En este caso:

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

2) Una clase de mamífero que se hereda de la clase animal

class Mammal < Animal def mammal_call protect_me end end

En este caso

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

3) Una clase de anfibios heredada de la clase Animal (igual que la clase de mamíferos)

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

En este caso

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

4) Una clase llamada Tree

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

En este caso:

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


Considera un método privado en Java. Puede ser llamado desde dentro de la misma clase, por supuesto, pero también puede ser llamado por otra instancia de esa misma clase:

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 } }

Entonces, si la persona que llama es una instancia diferente de mi misma clase, mi método privado es realmente accesible desde el "exterior", por así decirlo. Esto en realidad lo hace parecer no tan privado.

En Ruby, por otro lado, un método privado realmente debe ser privado solo para la instancia actual. Esto es lo que proporciona la eliminación de la opción de un receptor explícito.

Por otro lado, ciertamente debo señalar que es bastante común en la comunidad de Ruby no usar estos controles de visibilidad en absoluto, dado que Ruby te da formas de evitarlos de todos modos. A diferencia del mundo de Java, la tendencia es hacer todo accesible y confiar en que otros desarrolladores no lo arruinen.


Parte de la razón por la que se puede acceder a los métodos privados por subclases en Ruby es que la herencia de Ruby con clases es delgada. El módulo incluye: en Ruby, una clase, de hecho, es un tipo de módulo que proporciona herencia, etc.

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

Lo que esto significa es que, básicamente, una subclase "incluye" a la clase principal, de modo que las funciones de la clase principal, incluidas las funciones privadas , también se definen en la subclase.

En otros lenguajes de programación, llamar a un método implica hacer burbujear el nombre del método en una jerarquía de clases padre y encontrar la primera clase padre que responde al método. Por el contrario, en Ruby, aunque la jerarquía de clase principal todavía está allí, los métodos de la clase principal se incluyen directamente en la lista de métodos de la subclase que se ha definido.


protected métodos protected pueden ser llamados por cualquier instancia de la clase definitoria o sus subclases.

private métodos private se pueden llamar desde dentro del objeto que llama. No puede acceder directamente a los métodos privados de otra instancia.

Aquí hay un ejemplo práctico rápido:

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

some_method no puede ser private aquí. Debe estar protected porque lo necesita para admitir receptores explícitos. Sus métodos típicos de ayuda interna generalmente pueden ser private ya que nunca necesitan ser llamados así.

Es importante tener en cuenta que esto es diferente de la forma en que funcionan Java o C ++. private en Ruby es similar a protected in Java / C ++ en que las subclases tienen acceso al método. En Ruby, no hay forma de restringir el acceso a un método desde sus subclases como lo hace con private en Java.

La visibilidad en Ruby es, en gran medida, una "recomendación", ya que siempre puedes obtener acceso a un método que utiliza 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


Comparación de los controles de acceso de Java contra Ruby: si el método se declara privado en Java, solo se puede acceder mediante otros métodos dentro de la misma clase. Si un método se declara protegido, se puede acceder mediante otras clases que existen dentro del mismo paquete, así como por subclases de la clase en un paquete diferente. Cuando un método es público, es visible para todos. En Java, el concepto de visibilidad de control de acceso depende de dónde se encuentran estas clases en la jerarquía de herencia / paquete.

Mientras que en Ruby, la jerarquía de herencia o el paquete / módulo no encajan. Se trata de qué objeto es el receptor de un método.

Para un método privado en Ruby, nunca se puede llamar con un receptor explícito. Podemos (solo) llamar al método privado con un receptor implícito.

Esto también significa que podemos llamar a un método privado desde dentro de una clase en la que está declarado así como a todas las subclases de esta clase.

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

Nunca puede llamar al método privado desde fuera de la jerarquía de clases donde se definió.

El método protegido puede invocarse con un receptor implícito, como privado. Además, el método protegido también puede ser llamado por un receptor explícito (solamente) si el receptor es "propio" o "un objeto de la misma clase".

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

Resumen

Público: los métodos públicos tienen la máxima visibilidad

Protegido: el método protegido se puede invocar con un receptor implícito, como privado. Además, el método protegido también puede ser llamado por un receptor explícito (solamente) si el receptor es "propio" o "un objeto de la misma clase".

Privado: para un método privado en Ruby, nunca se puede llamar con un receptor explícito. Podemos (solo) llamar al método privado con un receptor implícito. Esto también significa que podemos llamar a un método privado desde dentro de una clase en la que está declarado así como a todas las subclases de esta clase.