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