¿Por qué Ruby tiene métodos privados y protegidos?
Antes de leer este artículo , pensé que el control de acceso en Ruby funcionaba así:
-
public
- puede ser accedido por cualquier objeto (por ejemplo,Obj.new.public_method
) -
protected
- solo se puede acceder desde el objeto en sí, así como cualquier subclase -
private
- igual que protected, pero el método no existe en subclases
Sin embargo, parece que protected
y private
actúan de la misma manera, excepto por el hecho de que no se puede llamar a private
métodos con un explícito receptor (es decir, self.protected_method
funciona, pero self.private_method
no).
¿Cuál es el punto de esto? ¿Cuándo hay un escenario en el que no desea que su método sea llamado con un receptor explícito?
6 answers
protected
los métodos pueden ser llamados por cualquier instancia de la clase definidora o sus subclases.
private
los métodos solo se pueden llamar desde el 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 estar private
aquí. Debe ser protected
porque lo necesita para soportar receptores explícitos. Sus métodos de ayuda internos típicos generalmente pueden ser private
ya que nunca necesitan ser llamados como este.
Es importante tener en cuenta que esto es diferente de la forma en que funciona Java o C++. private
en Ruby es similar a protected
en 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 puede hacerlo con private
en Java.
La visibilidad en Ruby es en gran medida una "recomendación" de todos modos, ya que siempre puede obtener acceso a un método utilizando 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
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-07-30 23:35:33
La diferencia
- Cualquiera puede llamar a sus métodos públicos.
- Puede llamar a sus métodos protegidos, o 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 se pueden llamar con un receptor implícito de
self
. Incluso tú no puedes llamarself.some_private_method
; debes llamarprivate_method
conself
implícito. (iGEL señala: "Hay es una excepción, sin embargo. Si tiene un método privado age=, puede (y tiene que) llamarlo con self 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 dependa de ello." Pero todavía obtienes las tijeras afiladas de send
y puedes llamar a cualquier método que quieras.
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>
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-05-23 12:10:26
Métodos privados en Ruby:
Si un método es privado en Ruby, entonces no puede ser llamado por un receptor explícito (objeto). Solo se puede llamar implícitamente. Puede ser llamado implícitamente por la clase en la que ha sido descrito, así como por las subclases de esta clase.
Los siguientes ejemplos lo ilustrarán mejor: {[15]]}
1) Una clase 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 Anfibios:
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 pueden ser llamados fuera de la jerarquía de la clase definidora.
Métodos protegidos en Ruby:
Si un método está protegido en Ruby, entonces puede ser llamado implícitamente tanto por la clase definidora como por sus subclases. Además, también pueden ser llamados por un explícito receptor siempre y cuando el receptor sea uno mismo o de la misma clase que el de uno mismo:
1) Una clase 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íferos que se hereda de la clase de animales
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>
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-05-20 20:05:19
Considere 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
}
}
Así que my si la persona que llama es una instancia diferente de mi misma clase my mi método privado es realmente accesible desde el "exterior", por así decirlo. Esto en realidad hace que no parezca tan privado.
En Ruby, por otro lado, un método privado realmente está destinado a 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, sin duda 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 sortearlos de todos modos. A diferencia del mundo Java, la tendencia es hacer que todo sea accesible y confiar en otros desarrolladores para no arruinar las cosas.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2010-08-20 20:27:23
Comparación de controles de acceso de Java contra Ruby: Si el método se declara privado en Java, solo se puede acceder a él por otros métodos dentro de la misma clase. Si un método es declarado protegido, puede ser accedido por 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 del control de acceso depende de dónde se encuentran estas clases en la herencia / paquete jerarquía.
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. Solo podemos llamar al método privado con un receptor implícito.
Esto también significa que podemos llamar a un método privado desde 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 puedes llame al método privado desde fuera de la jerarquía de clases donde fue definido.
Método protegido se puede llamar con un receptor implícito, como como privado. Además, el método protegido también puede ser llamado por un receptor explícito (solo) si el receptor es "self" 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 máxima visibilidad
Protected: Se puede llamar al método Protected con un receptor implícito, como si fuera private. En addition protected method también puede ser llamado por un receptor explícito (solo) si el receptor es "self" o "un objeto de la misma clase".
Private: Para un método privado en Ruby, nunca se puede llamar con un receptor explícito. Solo podemos llamar al método privado con un receptor implícito. Esto también significa que podemos llamar a un método privado desde una clase en la que está declarado, así como a todas las subclases de esta clase.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-07-18 03:50:49
Parte de la razón por la que los métodos privados pueden ser accedidos por subclases en Ruby es que la herencia de Ruby con clases es endulzar ligeramente sobre 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" la clase padre de modo que efectivamente las funciones de la clase padre, incluyendo funciones privadas , se definen en la subclase también.
En otros lenguajes de programación, llamar a un método implica burbujear el nombre del método en una jerarquía de clase padre y encontrar la primera clase padre que responda al método. Por el contrario, en Ruby, mientras que la jerarquía de la clase padre todavía está allí, los métodos de la clase padre se incluyen directamente en la lista de métodos de la subclase ha definido.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-02-21 01:27:12