At this point in the book, your programming style is starting to become more sophisticated. No longer are you writing code where one long file encompasses everything you do. Now you are using objects, and you are also beginning to program in an object-oriented manner. Object-oriented programming (OOP) is a powerful form of programming that is currently extremely popular and the backbone for many new languages, including Ruby.
Object inheritance is one of the most powerful parts of object-oriented programming. Inheritance enables hierarchical decomposition of structures into logically encapsulated units.
Thus far, we have talked about creating simple objects that are independent of one another. However, one of the most powerful abilities OOP has is the ability to define relationships between objects. The first of these relationships is known as inheritance. To understand the concept of inheritance, imagine a ball (any spherical object that could pass for a ball will do). Perhaps you are thinking of a baseball, a tennis ball, or a ping-pong ball; it does not matter which because all those balls have a large degree of similarity among them, despite being different types of balls or even different objects. If we needed to accomplish some task and asked you for a ball, they could all work despite their differences. Inheritance allows us to define these types of relationships with Ruby objects. This ends up saving the programmer significant time and code because she or he need not redefine parts of the objects that are similar.
Let’s return to our bank account example from the preceding chapter. It defined what is essentially a checking account, and our bank is no longer happy with just this one type of account; now it wants to expand to include savings accounts. The first thing to notice are the similarities between a savings account and a checking account: they both maintain a balance and can have money withdrawn from them and deposited to them. The class that defines the similarities in the relationship is referred to as the parent class, or the superclass.
The main differences between the two bank accounts are that you
cannot withdraw beyond the minimum balance from a savings account (we will
touch on this in the next section) and that a savings account generates
interest. The class that defines the differences in the relationship is
referred to as the child class or the subclass. Now
we will use inheritance to define this SavingsAccount
class (see Example 10-1).
1
require_relative
'../chapter_09/account_5.rb'
2
3
class
SavingsAccount
<
Account
4
def
initialize
(
balance
,
name
,
phone_number
,
interest
,
minimum
)
5
super
(
balance
,
name
,
phone_number
)
6
@interest
=
interest
7
@minimum
=
minimum
8
end
9
10
def
accumulate_interest
11
@balance
+=
@balance
*
@interest
12
end
13
end
Note that we use the require_relative
command instead of the require
command. require
loads files that are installed as Ruby
libraries or files for which the full path to the file is given. require_relative
is used to load files without
specifying the full path to the file; it looks for files in a location
relative to the file require_relative
is used in.
The first thing to note in the code provided in Example 10-1 is the <
symbol (on line 3). This is the symbol used
to define inheritance in Ruby. In this case, the parent class is the Account
class, which is predefined via
line 1, and the child class is the SavingsAccount
class. See the account
class in Example 9-9.
The next thing to look over is the constructor of the SavingsAccount
class, expressed by the initialize
method on line 4. Immediately, in
line 5, this method calls a method named super()
, which is the equivalent of calling the
initialize
method for the superclass
Account
. After this, we initialize the
instance variables @interest
and
@minimum
.
It is important to note that these pieces of data distinguish a SavingsAccount
from a CheckingAccount
and the subclass from the
superclass.
Finally, there is the accumulate_interest
method, which is just a
simple interest calculation.
However, thanks to inheritance, the SavingsAccount
class can do more than just
accumulate interest. It also inherits all the data and methods from the
Account
class. Table 10-1 is a summary of everything
inherited by the SavingsAccount
class.
Data |
Methods |
|
|
|
|
|
|
display |
If we create an instance of the SavingsAccount
class:
account
=
SavingsAccount
.
new
(
200
.
00
,
"Reynolds"
,
9694905555
,
0
.
015
,
150
)
we can then call any of the following methods:
account.deposit(amount) |
account.withdraw(amount) |
account.transfer(amount,
targetAccount) |
account.accumulate_interest |
account.display |
This explicitly shows the power of inheritance. Although we never
defined four of the five methods shown for the SavingsAccount
class, we can use them because
they are inherited from the parent class, Account
. The SavingsAccount
class consists of only 11 lines
of code, but it has as much functionality as 41 lines of code (the number
of lines of code in Example 10-1 plus
Example 9-9).
If we were to return to the transfer(amount, targetAccount)
method from the
preceding chapter we would see that
it was designed to transfer money from one account to another. At the time we created
the method, we had not designed a SavingsAccount
and were content with it
working only on Account
objects.
However, it will work on SavingsAccount
objects, because a SavingsAccount
is an
Account
; it has enough similarity for a
transfer between accounts to be possible. This is another powerful ability granted by inheritance, and
it is known as polymorphism. Polymorphism, however, does not work both
ways. With the transfer(amount,
targetAccount)
method example, the polymorphism is from the subclass to the
superclass. Polymorphism will not work when you are trying to morph from a
superclass to a subclass, because the subclass has abilities the
superclass does not. To express this in an example, imagine trying to call
the accumulate_interest()
method from
an Account
object; it won’t work
because only SavingsAccount
objects,
not Account
objects, have the accumulate_interest()
method.
3.17.157.6