OOP Advanced

Method Visibility

Public (default)

A public type attribute or method may be called during class definition (when you are defining the class), as well as outside of the class definition, like example_instance.public_method.

Protected

A protected type attribute or method may only be called during class definition, and it will be inherited by its subclasses.

Private

A private type attribute or method may only be called during class definition, but it will NOT be inherited by its subclasses.

Public Protected Private
Accessible outside Class Definition Yes No No
class Person
  def public_method
    puts "this is a public function"
  end

  protected

    def protected_method
      puts "this is protected function"
    end

  private

    def private_method
      puts "this is private function"
    end
end

new_person = Person.new

new_person.public_method
# => "this is a public function"
new_person.protected_method
# => NoMethodError: protected method `protected_method' called for #<Person:0x00>
new_person.private_method
# => NoMethodError: private method `private_method' called for #<Person:0x00>

The similarity in private and protected methods is that they are NOT publicly accessible. But, they are both accessible within the class definition.

Public Protected Private
Accessible inside Class Definition Yes Yes Yes

For example,

class Person
  def test_methods
    protected_method
    private_method
  end

  protected

    def protected_method
      puts "this is protected function"
    end

  private

    def private_method
      puts "this is private function"
    end
end

Person.new.test_methods
# => this is protected function
# => this is private function

Then, what is the difference between private and protected methods? Well, during class inheritance, protected methods will be inherited, but private methods won't.

Public Protected Private
Will be inherited by subclasses Yes Yes No
class Person
  protected

    def protected_method
      puts "this is protected function"
    end

  private

    def private_method
      puts "this is private function"
    end
end

class Student < Person
  def test_methods
    begin
      self.protected_method
      puts "protected methods will be inherited and works inside child class"
    end

    begin
      self.private_method # this will trigger "NoMethodError"
    rescue # rescue will catch the "NoMethodError" and print the following text
      puts "private methods will NOT be inherited and does NOT work inside child class"
    end
  end
end

Student.new.test_methods
# => this is protected function
# => protected methods will be inherited and works inside child class
# => private methods will NOT be inherited and does NOT work inside child class

Summary Table

Public Protected Private
Accessible outside Class Definition Yes No No
Accessible inside Class Definition Yes Yes Yes
Will be inherited by subclasses Yes Yes No

Instance vs Class Methods

Instance methods

By now, we know how to define an instance method.

class MyClass
  def instance_method
    puts 'this is an instance method'
  end
end

instance = MyClass.new # a new instance
instance.instance_method

But, in fact, we can also define a class method. Add self. in front of the method name to declare such method as a class method.

Class methods

class MyClass
  def self.class_method
    puts 'this is a class method'
  end
end

MyClass.class_method # we don't need an instance to call this method

Instance (@) vs Class Attributes (@@)

There are instance methods and class methods. Similarly, there are also instance attrbiutes and class attributes.

We know that to define an instance attribute, you put @ in front of a variable name.

class Dog
  def initialize(name)
    @name = name
  end
end

Now, we introduce class attributes, where you define name by putting @@ in front of a variable name.

For example, take a look at the following usage:

class Dog
  @@count = 0

  def initialize
    @@count += 1
  end

  def self.count # need this to retrieve the @@count
    @@count
  end
end

new_dog_1 = Dog.new
new_dog_2 = Dog.new
new_dog_3 = Dog.new
Dog.count
# => 3

The example above shows you that you can use a class attribute to keep track of the Dog population. Whenever a new dog instance is created, the total population count increments by 1. Since the Dog class represents the entire category of dogs, it only makes sense to put the population count as a class attribute.

An instance attribute represents a property of that single object. A class attribute represents a property of the entire category of objects.

Inheritance / Super

Say, you have a class ParentClass. And, you have a class SomeClass that will inherit ParentClass. Both ParentClass and SomeClass have a method named hello. What happens?

class ParentClass
  def hello
    puts 'hello method from ParentClass'
  end
end

class SomeClass < ParentClass
  def hello
    puts 'hello method from SomeClass'
  end
end

instance = SomeClass.new
instance.hello
# => hello method from SomeClass

Yes, SomeClass will completely overwrite ParentClass's hello method.

Now, there's a special case. Ruby introduced the super keyword, so that the same method in the ParentClass will be called once before being overwritten if you use the super keyword.

class ParentClass
  def hello
    puts 'hello method from ParentClass'
  end
end

class SomeClass < ParentClass
  def hello
    super
    puts 'hello method from SomeClass'
  end
end

instance = SomeClass.new
instance.hello
# => hello method from ParentClass
# => hello method from SomeClass

The super keyword will also pass all the arguments (if any) to ParentClass method.

class ParentClass
  def print(number)
    puts "ParentClass prints #{number}"
  end
end

class SomeClass < ParentClass
  def print(number)
    super
    puts "SomeClass prints #{number}"
  end
end

instance = SomeClass.new
instance.print(42)
# => ParentClass prints 42
# => SomeClass prints 42

results matching ""

    No results matching ""