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?