Ruby and Object Oriented Design
Ruby is an Object Oriented Design (OOD) language. Everything in Ruby is an Object and all other classes inherit their core functionality from the Object class. The classes, such as Hash, Array, String, Date, Numeric, and Time build on the Object class and then define how they work, what are their characteristics, and what they can do. Classes allow you create the same type of object repeatedly with ease. Users can also create their own classes to model other real world objects or situations.
Designing a Book Class
For this exploration of Ruby classes, I'm going to create a Book class. Classes are very easy to define in Ruby, so easy in fact that all you need is the following code:
class Book
end
Wasn't that incredibly simple to do? One thing to note is that class names must always start with a capital letter. Now the Book class exists, yet it doesn't really have any functionality yet. Let's fix that right now. When creating a new Book object, there are a couple of properties that should be initialized, specifically the values for a title and an author. In order to accomplish this I'm going to define an initialize method, that will accept two arguments, title and author. This way when we create a new Book object, these values will be saved and accessible within the object. The following code snippet shows the initialize method getting defined.
class Book
def initialize(title, author)
@title = title
@author = author
end
end
This looks great and when a new Book is created it will store the title and the author. Looking over this code you might be wondering what the @
symbol in front of the variable names in the initialize method are doing there. Well they do serve a purpose that I will explain now.
Instance and Class Variables
The @
symbol in front of title and author, denote those variables as instance variables. An instance variable is a variable that can be accessed throughout each instance of a specific class. Looking at the example that we're using right now, while each new instance of the Book class would have a title and author, those items would be unique for each instance. If we wanted all instances of the Book class to share specific information, we would need to use a class variable. A class variable is declared by putting @@
in front of the variable name, e.g. if we wanted all instances of our Book class to share the same language we could do the following:
@@language = "English"
If this were applied to our Book class, each instance of the Book class would have a unique title and author, but they would all share the same language, English. Now that class and instance variables have been discussed, let's make some additional changes to the Book class.
Getter, Setter, and Attribute Methods
As the program stands now the title and author can't be accessed. What happens if those values need to be changed or their contents seen? Well one method of dealing with that is to create methods to get and set the values. Let's create the getter and setter values for the Book class.
class Book
def initialize(title, author)
@title = title
@author = author
end
def title
@title #=> Getter Method
end
def title=(title)
@title = title #=> Setter Method
end
def author
@author #=> Getter Method
end
def author=(author)
@author = author #=> Setter Method
end
end
Now anyone that uses the book class can get and set both the title and the author each instance. That's a lot of code for rather simple and repetitive tasks; fortunately the Ruby developers realized this and created the three accessor methods that perform the same tasks: reader, writer, and accessor. These methods are created in the background when a new Class instance is created. The attribute reader method creates a getter method, while the writer method creates a setter method, and the accessor method creates both a getter and setter method. The accessor methods are created with the following syntax:
attr_reader :var_name
attr_writer :var_name
attr_accessor :var_name
For the Book class I don't want people to be able to change the title and author after the instance of the class has been created. Instead of having both the setter and getter methods in the code for this class, I will change them to be attribute reader methods, as seen in the next code segment. Additionally I want users to be able to add a review to each Book, so I will use an attribute accessor method.
class Book
attr_reader :title, :author
attr_accessor :review
def initialize(title, author)
@title = title
@author = author
end
end
Instance Methods
Finally I want each instance of the Book class to neatly print out the title, author, and the review if on has been added in one line. This can be accomplished by creating an instance method, which will be available to each instance of the class it is defined within. Here is an example of that and how all of the previous information can be used as well.
class Book
attr_reader :title, :author
attr_accessor :review
def initialize(title, author)
@title = title
@author = author
end
def print_book_info
puts "Title: #{self.title}"
puts "Author: #{self.author}"
puts "Review: #{self.review || "This item hasn't been reviewed yet."}"
end
end
wise_mans_fear = Book.new("Wise Man's Fear", "Patrick Rothfuss")
wise_mans_fear.print_book_info #=> "Title: Wise Man's Fear"
#=> "Author: Patrick Rothfuss"
#=> "Review: This item hasn't been reviewed yet."
wise_mans_fear.review = "Another fascinating read by Patrick Rothfuss. This " +
"entry in the King Killer Chronicles did not have the big dramatic conclusion " +
"that I had been hoping for myself."
wise_mans_fear.print_book_info #=> "Title: Wise Man's Fear"
#=> "Author: Patrick Rothfuss"
#=> "Review: Another fascinating read by Patrick
# Rothfuss. This entry in the King Killer
# Chronicles did not have the big dramatic
# conclusion that I had been hoping for myself."
Closing Thoughts
Object Oriented Design (OOD) is a powerful tool that can be used to mimic real life situations and real life concepts. When first creating classes it might be very difficult to be able to break things down appropriately, but with continued use, it becomes much easier. With larger projects, familiarity and understanding of OOD is a exceptionally useful, if not required.