There are three types of Ruby instance methods: public, private, and protected. Different types of methods have different access constraints. In this article, I mainly want to introduce the differences between them in detail.
1. Public methods
Instance methods defined directly in class lexical scope are public methods. Public methods can be called by other instance methods inside the scope of class lexical, or they can be created outside the scope and called directly, for a brief example
class Person
def initialize(first_name, last_name)
@first_name = first_name
@last_name = last_name
end
def message
"Hello #{full_name}!!!!!!!!!"
end
def full_name
"#{@first_name} #{@last_name}"
end
end
@p = Person.new("zhiheng"."Lan")
puts @p.full_name # => zhiheng Lan
puts @p.message # => Hello zhiheng Lan!!!
Copy the code
The Person#message and Person#full_name defined above are both public instance methods.
- We can instantiate
Person
Class, directly through the instance variable@p
To directly callPerson#full_name
Methods. - When an instance variable
@p
Explicitly callPerson#message
The method is called internallyPerson#full_name
Methods.
2. Private methods
Many times we do not want to expose instance methods outside of lexical scope and call them directly from the created instance. At this point, consider making these methods private.
Unlike the Java language, in Ruby we do not need to define private methods with the private keyword in front of each method. Instead, we simply allocate a private area in which all private methods are written. Next we set the original Person#full_name method to private and add a private method named Person#greet
class Person
def message
"#{greet} #{full_name}!!!!!!!!!"
end
private
def full_name
"#{@first_name} #{@last_name}"
end
def greet
"Hi,"
end
end
@p = Person.new("zhiheng"."Lan")
puts @p.greet
#=> NoMethodError: private method `greet' called for #<Person:0x00007f8598011dd0>
puts @p.full_name
# => NoMethodError: undefined method `full_message' for #<Person:0x00007f8598011dd0>
puts @p.message
# => Hi, zhiheng Lan!!!
Copy the code
PS: Indentation of code in private areas seems to be a common practice in the community.
As you can see, the Person#greet and Person#full_name methods can no longer be called directly from the instance because they have been privatized. But Person#full_name can still be called by the Person#message instance method inside the class lexical scope.
3. Protected methods
The distinction between protected and private methods is subtle. The common point between them is that they cannot be called directly by an instance outside of class lexical scope, but it is more constrained than protecting methods
In class lexical scope, we cannot give private methods an exact receiver.
For example, define a private method and a protected method, and call them from another instance method inside the class lexical scope
class Person
def call_method
puts protected_method
puts private_method
puts self.protected_method
puts self.private_method # will raise error
end
private
def private_method
"I am private method"
end
protected
def protected_method
"I am protected method"
end
end
@p = Person.new("zhiheng"."Lan")
@p.call_method
# => I am protected method
# => I am private method
# => I am protected method
# => NoMethodError: private method `private_method' called for #<Person:0x00007f8598011dd0>
Copy the code
As you can see, if you add an explicit receiver self to a private method, it will report an error, while the protected method will work in this case, which is the biggest difference between them.
As another example, add a method Person#== to determine whether two Person instances are the same Person (as long as Person#full_name is the same).
class Person
def = =(other)
self.full_name == other.full_name
end
end
@lan = Person.new("zhiheng"."Lan")
@liang = Person.new("haidao"."Liang")
puts @lan == @liang
# => NoMethodError: private method `full_name' called for #<Person:0x00007f85990f91c0>
Copy the code
Since the previous Person#full_name method was defined as private, specifying the receiver self explicitly at this point will throw an exception. The solution is to redefine it as a protection method
class Person
def = =(other)
self.full_name == other.full_name
end
protected
def full_name
"#{@first_name} #{@last_name}"
end
end
@lan = Person.new("zhiheng"."Lan")
@liang = Person.new("haidao"."Liang")
puts @lan == @liang
# => false
Copy the code
Protected methods may not have as many applications as public and private methods, but it’s important for us Ruby programmers to understand the differences.
4. Constraint phenomenon in inheritance relationship
In Ruby, instance methods of all three types can be called directly in the lexical scope of a subclass, acting as if related methods were defined in the subclass as well. Let’s take a simple example
class Animal
def initialize(name, age)
@age = age
@name = name
end
def name
@name
end
private
def greet
"hello world"
end
protected
def age
@age
end
end
class Cat < Animal
def test
puts name
puts greet
puts age
end
end
Cat.new("tom".1).test
# tom
# hello world
# 1
Copy the code
So even the private Animal#greet method can be called directly from the lexical scope of the subclass even after the class is inherited. Now LET me define two instance methods in the Cat class to see something a little more interesting
.class Cat
def private_or_protected
puts greet # 4
puts Animal.new('James'.24).age # 2
puts Animal.new('James'.24).greet # 3
end
private
def greet # 5
"hello cat"
end
end
puts Animal.new('James'.24).age # 1
# NoMethodError: protected method `age' called
Cat.new("tom".1).private_or_protected
# hello cat
# 24
# NoMethodError: private method `greet' called
Copy the code
In contrast to #1 and #2, Animal#age is a protected method that cannot be called directly by instance in the top-level scope. However, if we instantiate Animal in Cat’s lexical scope, we can call Animal#age directly.
In contrast to #2 and #3, the private method Animal#greet cannot be called directly by an instance of the parent class in the lexical scope of a subclass like the protected method Animal#age. After all, we cannot add an explicit receiver to a private method.
The private Animal#greet method of the parent can be overridden in snippet #5 and called directly in snippet #4.
5. Are Ruby private methods really that private?
Ruby gives programmers the most freedom, and public and private methods in Ruby are more of a taxonomy than a constraint. Outside of the lexical scope, objects can still call private methods directly through the Object#send method, as shown in the following example
class Say
private
def hello
"Hello World"
end
end
@s = Say.new
@s.hello
# => NoMethodError: private method `hello' called for #<Say:0x007fc44e929130>
@s.send(:hello)
# => "Hello World
Copy the code
It is very convenient to debug methods using this dark technology.
You can do anything, but you have to know what you’re doing.
The end of the 6.
This chapter focuses on the differences between private, public, and protected instance methods in Ruby. The private methods in Ruby are not that private, and we can still call them directly through some dark technology if necessary.
reference
-
The difference between Public, Protected and Private methods in Ruby
-
Are there good reasons for ‘private’ to work the way it does in Ruby?
-
Is Ruby private method accessible in sub class?