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.

  1. We can instantiatePersonClass, directly through the instance variable@pTo directly callPerson#full_nameMethods.
  2. When an instance variable@pExplicitly callPerson#messageThe method is called internallyPerson#full_nameMethods.

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?

Happy Coding and Writing !!!