18 Sep Ruby Part 3, Continue using classes (using self, super, inheritance, encapsulation)
Let’s continue with using classes
Encapsulation
Let’s see some example
send_post(“the title”, 14) def send_post(title, owner_id) retrieve_owner(owner_id) end
Now we leave all the work to the class
post = Post.new post.title = “Ruby classes” post.owner = current_user.id send_post(post.title) def send_post(title) title.owner end class Post attr_accessor :name def owner retrieve_user(owner_id) end end
Visibility
By default in ruby the methods are public for example
class Post
def up_vote(friend)
bump_karma
friend.bump_karma
end
def bump_karma
puts “Karma upvote for #{name}”
end
end
In the before example we can see that all methods are public, but the bump_karma method it doesnt be public, then we need to change the method to private method, but if we declared the method “private” we can’t call the methods with explicit receiver and for that we gonna declare the method as “protected” to prevent to use from outside and we want use only inside, from other instance in the same class.
class Post
def up_vote(friend)
bump_karma
friend.bump_karma
end
protected
def bump_karma
puts “Karma upvote for #{name}”
end
end
Inheritance
Let’s continue with the inheritance, and in the next example we can see how we have a duplicated code and later we can see when we use inheritance we can avoid duplicated code
Using duplicated code
class Image
attr_accessor :title, :size, :url
def to_s
“#{@title}, #{@size}”
end
end
class Video
attr_accessor :title, :size, :url
def to_s
“#{@title}, #{@size}”
end
end
Now using inheritance we can avoid duplicate code
class Attachment
attr_accessor :title, :size, :url
def to_s
“#{@title}, #{@size}”
end
end
Then at the momento to use inheritance
class Video < Attachment end class Image < Attachment end
Then we can use to_s method in both classes and if we want to add some specific attribute to specific sub-class and we will avoid the duplicate code.
SUPER
We can see in the next example, if we don’t use the @name = name the variable won’t show the name but if we use the super(name) automaticly the name will be inititialized by the super class.
class User
def initialize(name)
@name = name
end
end
class Follower < User
def initialize(name, follower)
#@name = name
#super(name)
@follower = follower
end
def relations
puts "#{@name} is followed by #{@follower}"
end
end
UserTest = Follower.new("Heriberto","Marios")
UserTest.relations
Some example more advanced, we can see in the next example, where the child invoke super the first search for that string in the Parent and if don’t find any method with the called name, it will continue with the search in the grandparent class.
class Grandparent
def my_method
"String called from grandparent"
end
class Parent < Grandparent
end
class Child < Parent
def my_method
string = super
puts "#{string} called from child method"
end
end
Son = Child.new
Son.my_method
Overriding Methods
<strong> </strong>class Attachment def preview "video" end end class Image < Attachment def preview "Image" end end image = Image.new puts image.preview
This will print
Image
Hide Instances variables
We can see the duplicated code in the next example
class Tweet
def initialize(first, last)
@first = first
@last = last
end
def tweet_content(description)
[@first, @last].compact.join(", ") + " Say \'" + description + "\'"
end
def Profile
[@first, @last].compact.join(", ")
end
end
tweet = Tweet.new("heriberto", "perez")
puts tweet.tweet_content "Some description"
Now let’s go to refactor the below code hiding variables
class Tweet
def initialize(first, last)
@first = first
@last = last
end
def name
[@first, @last].compact.join(", ")
end
def tweet_content(description)
name + " Say \'" + description + "\'"
end
def Profile
name
end
end
tweet = Tweet.new("heriberto", "perez")
puts tweet.tweet_content "Some description"
Exercises to practice a little
Collection Class
Managing our game library is getting a little difficult with all of these game instances floating around. Let’s create a newLibrary class which will manage a collection of Game objects. Create a Library class whose initializer stores a gamesarray. Ensure games is publicly accessible.
Final code:
class Library attr_accessor :games def initialize(games) @games = games end end
Encapsulation
We got a little ahead of ourselves and added a has_game? method to Library that takes in the name of a game. Then, we realized that it doesn’t compare year or system! Rather than passing in a game name to the has_game? method, pass in an instance of a game, and check for equality with the entire game object using the declared == method on Game.
class Library
attr_accessor :games
def initialize(games)
self.games = games
end
def has_game?(search_name)
for game in games
return true if game.name == search_name
end
false
end
end
<strong>Final Code</strong>
class Library
attr_accessor :games
def initialize(games)
self.games = games
end
def has_game?(search_name)
for game in games
return true if game == search_name
end
false
end
end
Instance Method
We can initialize our Library with an array of games, but the only way to add games from outside the class is to use thegames accessor method and alter the array. This is breaking encapsulation, so let’s create a new method in Librarycalled add_game which takes in a game and adds it to the games array.
origin code:
class Library attr_accessor :games def initialize(games) self.games = games end def has_game?(search_game) for game in games return true if game == search_game end false end end
class Library attr_accessor :games def initialize(games) self.games = games end def has_game?(search_game) for game in games return true if game == search_game end false end def add_game(game) games << game end end
class Library attr_accessor :games def initialize(games) self.games = games end def has_game?(game) for game in games return true if game == search_game end false end def add_game(game) games << game log(game.name) end private def log(string) puts string end end
For our ArcadeGame class, we’ll also want to track the weight of these giant cabinets taking up all of our available space. Luckily we thought ahead: we already take in an options parameter that we can stick weight into! Override theinitialize method for ArcadeGame to take in the same parameters as its parent class, call super, and then setweight.
class ArcadeGame < Game
attr_accessor :weight
def initialize(name, options={})
super
@weight = options[:weight]
end
end
class ConsoleGame < Game
end
Whenever we output a game right now it’ll show up using the to_s method from Object, the parent object of Game. A basic to_s implementation is completed below on Game. Override this for ConsoleGame to also show the system the game is on.
class Game
attr_accessor :name, :year, :system
attr_reader :created_at
def initialize(name, options={})
self.name = name
self.year = options[:year]
self.system = options[:system]
self.created_at = Time.now
end
def to_s
self.name
end
end
class ConsoleGame < Game
def to_s
@name + @system
end
end
Refactoring
Our to_s method will come in very handy. Whenever we need to output a game, rather than calling a method on the game, we can just output the game object and Ruby will call to_s on it automatically. Refactor both classes below. Change thedescription method of Game to use the to_s method implicitly. Then remove any duplicated code in ConsoleGame. Note: you’ll need to use self inside a class to reference the entire object.
Origin code:
class Game
attr_accessor :name, :year, :system
attr_reader :created_at
def initialize(name, options={})
self.name = name
self.year = options[:year]
self.system = options[:system]
@created_at = Time.now
end
def to_s
self.name
end
def description
"#{self.name} was released in #{self.year}."
end
end
class ConsoleGame < Game
def to_s
"#{self.name} - #{self.system}"
end
def description
"#{self.name} - #{self.system} was released in #{self.year}."
end
end
Final code:
class Game
attr_accessor :name, :year, :system
attr_reader :created_at
def initialize(name, options={})
self.name = name
self.year = options[:year]
self.system = options[:system]
@created_at = Time.now
end
def to_s
self.name
end
def description
"#{self} was released in #{self.year}."
end
end
class ConsoleGame < Game
def to_s
name = super
"#{name} - #{self.system}"
end
end
Angel Solorio
Posted at 02:13h, 18 JulyWhat’s the different between use @variable_name instead of self.variable_name?