codedecoder

breaking into the unknown…

difference between private protected public in ruby

2 Comments

private, protected and public control the visibility of methods in ruby class .By default methods in ruby are public except global and initialize method which are private by default. As far as variable are concerned they are private by default as variable can’t be seen by them self but through getter and setter method defined for a variable. you can define the getter and setter method yourself, which is not actually needed as it can be achieved by using :attr_accessor. the variables for which you have defined getter and setter methods behave as public. constants are effectively public , there is no way to define a constant that is inaccessible to outside use. we can say that public, private and protected apply only to methods

private, protected and public are not any keyword but actual method of object class that operate on any other class, dynamically altering the visibility of its methods . you can get the list of private, protected or public method of a class by using private_methods , protected_methods and public_methods on it.

private, protected and public take methods name as argument. If you do not pass argument all the method following them will have the same visibility until it is changed by applying the other. Let us illustrate it with examples in visibility_demo.rb files

# this global method defined outside class so private
def iam_global
  puts "Iam global"
end

class VisibilityDemo

  def initialize
    @message = "Iam the class demonstrating visibility in ruby"
  end
  # No visibility specifier given so public by default

  def pub_method
    puts "Iam public"
  end

  private
  # This method appears after the private' visibility specifier so private
  def pri_method
    puts "Iam private"
  end

  # This method is also private because the previous visibility not overridden
  # specifier has not been overridden
  def pri_method2
    puts "Iam private"
  end

  protected
  # This method appears after the protected visibility specifier so protected
  def pro_method
    puts "Iam protected"
  end

  public
  #this method overridden above protected specifier and become public
  def pub_method2
    puts "Iam public"
  end
end

The risk in above way of defining the visibility (visibility specifier without arguments) is that all following method will have same behaviour(try by changing the position of say private, keep it at top), which we not always want, so it is better to use them in argument form if the list of particular visibility is not very long. Let us redefine it in argument form and also add some other piece of code, needed to demo visibility behaviour.

def iam_global
  puts "Iam global"
end

class VisibilityDemo

  def initialize
    @message = "Iam the class demonstrating visibility in ruby"
  end

  def pub_method
    puts "#{self}"
    puts "Iam public"
  end

  def pri_method
    puts "Iam private"
  end

  def pri_method2
    puts "Iam private"
  end

  def pro_method
    puts "Iam protected"
  end

  def demo_explicit_object_calling_pro(obj)
    #call the protected method on explcit object
    obj.pro_method
  end

  def pub_method2
    puts "Iam public"
  end

  #specifying visibility specifier with argument
  private :pri_method, :pri_method2
  protected :pro_method
end

#class inheriting from VisibilityDemo class
class InheritVisibility < VisibilityDemo
  # the will call methods of parent class with self as explicit receiver
  def call_methods_with_self
    self.pub_method
    self.pro_method
    self.pri_method
  end

  # the will call methods of parent class without any receiver
  def call_methods_without_receiver
    pub_method
    pro_method
    pri_method
  end

  #overriding private method of parent class
  def pri_method2
   puts "I have overrided parent and become public"
  end

end

#class not inheriting from VisibilityDemo
class AnyClass
  def call_methods
    pub_method
    pro_method
    pri_method
  end  
end

Now let us list all the types of method on irb console
> load “/home/arun/Desktop/visibility_demo.rb” #load the visibility_demo.rb file
=> true
> @visbiility_demo = VisibilityDemo.new # create instance of VisibilityDemo class
=> #
> @visbiility_demo.public_methods # list the public methods
=> [:pub_method, :pub_method2,...]
> @visbiility_demo.protected_methods # list the protected methods
=> [:pro_method]
> @visbiility_demo.private_methods  # list the private methods
=> [:initialize, :pri_method, :pri_method2, :iam_global, ..] # see that initialize and iam_global listed under private

O.K, so we now know how to set the visibility of a method. Let us now see how the visibility specifier change the behaviour of methods in its own class and its inherited classes.

NOTE : before you proceed you must be clear on self and implicit and explicit receiver of a method . You should also know that, there is three way of calling any method : method_name , self.method_name, @object.method_name

1 => irrespective of its visibility any method can be called only in its class hierarchy

See the below output on irb :

> @any_class = AnyClass.new # create instance of any class
 => #<AnyClass:0x942b25c>
> @any_class.pub_method # call the pub method of VisibilityDemo class
NoMethodError: undefined method `pub_method’ for #<AnyClass:0x942b25c> #give error , can’t be called
> @any_class.pro_method # call the protected method of VisibilityDemo class
NoMethodError: undefined method `pro_method’ for #<AnyClass:0x942b25c> #give error , can’t be called
> @any_class.pri_method # call the private method of VisibilityDemo class
NoMethodError: undefined method `pri_method’ for #<AnyClass:0x942b25c> #give error , can’t be called
> @any_class.call_methods # call method of AnyClass calling VisibilityDemo class method implicitly
NoMethodError: undefined method `pub_method’ for #<AnyClass:0x942b25c>

2 => Public method can be called on any object in the class hierarchy as the explicit receiver, but protected and private can’t

See the below output on the irb console :

> self
=> main
> @visbility_demo = VisibilityDemo.new # creating object of  VisibilityDemo class
 => #<VisibilityDemo:0x970c214 @message=”Iam the class demonstrating visibility in ruby”>
> @visbility_demo.pub_method #call the public method
Iam public
> @visbility_demo.pro_method #call the protected method
NoMethodError: protected method `pro_method’ called for #<VisibilityDemo:0x970c214> # give error
> @visbility_demo.pri_method #call the private method
NoMethodError: private method `pri_method’ called for #<VisibilityDemo:0x970c214> # give error

NOTE : Here there is one exception for protected method, protected method can also be called on a object if its belongs to the same class as self.

In the above output , you are running the code from irb and so at this point of program self is main, but the object @visbility_demo belongs to VisibilityDemo class, since the class of self and the class of object is different, the protected method throw error. See the below output where protected method called on explicit object and worked fine.

> @visbiility_demo = VisibilityDemo.new # creating object of VisibilityDemo
 => #<VisibilityDemo:0x955e034 @message=”Iam the class demonstrating visibility in ruby”>
> obj = VisibilityDemo.new # creating another object of VisibilityDemo
 => #<VisibilityDemo:0x9531408 @message=”Iam the class demonstrating visibility in ruby”>
> @visbiility_demo.demo_explicit_object_calling_proc(obj) #for demo_explicit_object_calling_proc method self is VisibilityDemo and since object passed to it is also of same class, the protected method worked this time
Iam protected

3 => Public and Protected both can be called with self as explicit receiver but Private can’t

See the below output on the irb console

> @inherting_visibility = InheritVisibility.new # create instance of the inheriting class
 => #<InheritVisibility:0x96e068c @message=”Iam the class demonstrating visibility in ruby”>
> @inherting_visibility.call_methods_with_self # call the method calling parent class method with self
Iam public # public method called successfully with self
Iam protected # protected method called successfully with self
NoMethodError: private method `pri_method’ called for #<InheritVisibility:0x96e068c> # private method give error

4 => public protected and private all can be called implicitly i,e without any receiver

See the below output on irb console:

> @inherting_visibility = InheritVisibility.new
 => #<InheritVisibility:0x96e068c @message=”Iam the class demonstrating visibility in ruby”>
> @inherting_visibility.call_methods_without_receiver
Iam public
Iam protected
Iam private

5 => If a private method overridden in its subclass, the method exhibit visibility as defined in subclass

see the below output in irb console

> @visibility_demo = VisibilityDemo.new # create object of VisibilityDemo class
 => #<VisibilityDemo:0x9e49734 @message=”Iam the class demonstrating visibility in ruby”>
> @visibility_demo.pri_method2 # call the private method
NoMethodError: private method `pri_method2′ called for #<VisibilityDemo:0x9e49734> # error as expected
> @inherit_visibility = InheritVisibility.new # create object of subclass of VisibilityDemo class which override the method
 => #<InheritVisibility:0x9f01e4c @message=”Iam the class demonstrating visibility in ruby”>
> @inherit_visibility.pri_method2 # call the private method of parent again
I have overrided parent and become public # it worked this time as it is overridden in the subclass 

So, from the above points we can summarized the behaviour as below :

Private method can be called only implicitly (i,e without any receiver) . Protected method can be called with a explicit receiver but that should be self or if a object, the object must belong to same class as self . Public method can be called in any way in its class hierarchy .

So through above point ruby ensure that private method should never we called on a object and let ruby invoke it by its own. Remember that you can create instance of any class anywhere and if a highly secure method say which manipulate user balance, call it current_account_balance is not private, then any person can misuse it by creating the object of class and calling the method.

Here, ruby differ in private implementation in C and c++ . In C ++  “private” means “private to this class”, while in Ruby it means “private to this instance”. What this means, in C++ from code in class A, you can access any private method for any other object of type A. In Ruby, you can not: you can only access private methods for your instance of object, and not for any other object instance (of class A).

It should be also noted that, ruby prevent accidental misuse of private method, but not prevent it strictly. So if you want, you can invoke a private method on an object also with send, see the below output on console

> @visibility_demo = VisibilityDemo.new
 => #<VisibilityDemo:0x9e8c804 @message=”Iam the class demonstrating visibility in ruby”>
> @visibility_demo.pri_method # trying to call the private method on object
NoMethodError: private method `pri_method’ called for #<VisibilityDemo:0x9e8c804> #throw no method error
> @visibility_demo.send(:pri_method) # # trying to call the private method on object with send
Iam private #it worked

Since, private method can’t be called on its own object also, but there are some cases where we me need it, so there we can make the method protected. Let us take a simple example below in file say my_bank.rb

class BankRate

  def initialize(bank_name, rate)  
    @bank_name = bank_name
    @rate      = rate  
  end 

  def bank_name
     @bank_name
  end 

  def current_rate  
    @rate  
  end 

  def comparative_rate(bank)  
    if bank.current_rate > current_rate  
      "#{bank.bank_name} have higher rate then #{@bank_name}"  
    else  
      "#{bank.bank_name} have higher rate then #{@bank_name}"  
    end  
  end 

  private :current_rate  
end  

# call the comprative_rate method
@sbi = BankRate.new("sbi", 12)  # first object of BankRate class
@icici = BankRate.new("icici", 14)  # secone object of same BankRate class
puts  @sbi.comparative_rate(@icici) # calling comprative_rate method on first object and passing second as argument

So, we have a BankRate class dealing with bank rate. We want that, the current_rate should not be misused so we have made it private. This class also have a method which compare the rate of the bank with another bank . Try to load the my_bank.rb file on console

> load “/home/arun/Desktop/my_bank.rb” #load my_bank.rb file
NoMethodError: private method `current_rate’ called for #<BankRate:0x9f90d90 @bank_name=”icici”, @rate=14>

So you can see that, although the @sbi  and @icici bot are object of the same class, still they can’t invoke the private method. Now change the visibility of current_method to protected in above file i,e protected :current_rate and run it again.

> load “/home/arun/Desktop/visibility_demo.rb” #load my_bank.rb file again
icici have higher rate then sbi # it worked this time

So, this worked this time. This demonstrate when to use private and when to use protected.

Reference :

http://en.wikibooks.org/wiki/Ruby_Programming/Syntax/Classes

http://lylejohnson.name/blog/2003/12/09/private-methods-in-c-and-ruby/ # comparison with c, c++

About these ads

Author: arunyadav4u

over 5 years experience in web development with Ruby on Rails.Involved in all stage of development lifecycle : requirement gathering, planing, coding, deployment & Knowledge transfer. I can adept to any situation, mixup very easily with people & can be a great friend.

2 thoughts on “difference between private protected public in ruby

  1. What’s up to every one, the contents present at this website are truly awesome for
    people experience, well, keep up the nice work fellows.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

Follow

Get every new post delivered to your Inbox.