¿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 aself.some_private_method
; debe llamar aprivate_method
conprivate_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.