Objects are made up of two important concepts: the data the object holds (the instance variables) and the actions the object can perform (the methods).
The previous section detailed how to create and instantiate an
object of an Account
class, and while
reading it you might have been thinking to yourself, “How was I supposed
to know that an Account
class needs a
balance
variable?” The answer is that
no class requires any particular piece of data, but classes are used to
group related pieces of data together, and it only makes sense that an
account has a balance. Likewise, there are other things that would come
as a part of a bank account. Depending on the nature of the bank
account, the type of data included would change. For example, a savings
account wouldn’t necessarily include the same data as a checking
account. Regardless, we are talking about a generic bank account, and
additional data possibly included are name, phone number, Social
Security number, minimum balance, and maximum balance. We now introduce
two additional instance variables to our Account
class, as shown in Example 9-3.
1
class
Account
2
def
initialize
(
balance
,
name
,
phone_number
)
3
@balance
=
balance
4
@name
=
name
5
@phone_number
=
phone_number
6
end
7
end
Note that the instance variables balance
, name
, and phone_number
are assigned in the order the
parameters were passed to the initialize
method; however, this is not
required. It is done merely for convenience to the reader. Also, the
actual names can be chosen arbitrarily. For example, @cash = balance
, while allowed, is
discouraged.
Our bank account is beginning to make a bit more sense. On top of
just having a balance, there is a name and a phone number attached to
the account, so we can uniquely determine whose account it is. Now that
our Account
class constructor has
changed, let’s see how to initialize Bob’s bank account when he has $10
as his starting balance, and has a phone number of 716-634-9483.
bob
=
Account
.
new
(
10
.
00
,
"Bob"
,
7166349483
)
An object also contains methods. Just like data, methods are
logically grouped together based on the class. The purpose of a method
is to accomplish a task, so we must ask ourselves, what actions should a
bank account have? We would expect that at the very least we could
withdraw from and deposit to our account. Let’s add these methods to our
Account
class, as shown in Example 9-4.
1
class
Account
2
def
initialize
(
balance
,
name
,
phone_number
)
3
@balance
=
balance
4
@name
=
name
5
@phone_number
=
phone_number
6
end
7
8
def
deposit
(
amount
)
9
# code
10
end
11
12
def
withdraw
(
amount
)
13
# code
14
end
15
end
Aside from the missing implementation code on lines 9 and 13, our
Account
class implementation is
looking pretty good. Not only can Bob open an account, but he can also
deposit or withdraw money when he desires.
The Account
class is almost
finished, and the only thing left to do before Bob is able to open a
bank account is to implement the deposit and withdraw methods.
A key advantage of objects is that they abstract the details of
their operations away from the code that uses them. Once the details of
the Account
class are finalized, a
programmer can use the class without knowing any of those details. The
programmer need only know what data are required to initialize the
class, and what data are required for each method in the class. For
example, consider the String
class
provided in the Ruby standard library. When we use the capitalize
method, we do not know how String
stores the data, nor how the data get
accessed. All we need to know is that the capitalize
method capitalizes the first letter
of the string.
As we implement an object, we must consider every detail of its
operation. The deposit
method, for example, must add
the value of the parameter passed to the previous @balance
and store the result back in @balance
. Let’s take a look at the
implementation of the deposit and withdraw methods, as shown in Example 9-5.
1
class
Account
2
def
initialize
(
balance
,
name
,
phone_number
)
3
@balance
=
balance
4
@name
=
name
5
@phone_number
=
phone_number
6
end
7
8
def
deposit
(
amount
)
9
@balance
+=
amount
10
end
11
12
def
withdraw
(
amount
)
13
@balance
-=
amount
14
end
15
end
Recalling our earlier Gem of Wisdom and looking at Example 9-5, using our shorthand construct
known as op=
, in line 9, the
variable @balance
is incremented by
the value of amount
, meaning
@balance = @balance + amount
, and
in line 13, the meaning of the statement is @balance = @balance - amount
.
To use these newly defined methods, we must initialize the classes
and then access them as we did with built-in methods. Note in the
following code that this is the first time we import definitions using
the require
command. For example, to
create an account for Mary, with $500, and then to deposit another $200,
we would perform the following steps in irb
:
irb
(
main
):
003
:
0
>
require
'account_4.rb'
=>
true
irb
(
main
):
004
:
0
>
mary_account
=
Account
.
new
(
500
,
"Mary"
,
8181000000
)
=>
#<Account:0x3dfa68 @balance=500, @name="Mary", @phone_number=8181000000>
irb
(
main
):
005
:
0
>
mary_account
.
deposit
(
200
)
=>
700
irb
(
main
):
006
:
0
>
mary_account
=>
#<Account:0x3dfa68 @balance=700, @name="Mary", @phone_number=8181000000>
As can be seen from the output, Mary’s account now holds 700 in
its @balance
variable. However, it
would be much nicer to provide a helper method to display this
information. The display
method is an
often-used method for outputting the contents of an object’s instance.
For the Account
class, we can output
the name, phone number, and account balance to the screen with the code
shown in Example 9-6.
1
def
display
()
2
puts
"Name: "
+
@name
3
puts
"Phone Number: "
+
@phone_number
.
to_s
4
puts
"Balance: "
+
@balance
.
to_s
5
end
Now we can immediately see the result of our actions. For example, try running the following code, which indirectly transfers $200 from Bob’s account to Mary’s:
bob_account
=
Account
.
new
(
500
,
"Bob"
,
8181000000
)
mary_account
=
Account
.
new
(
500
,
"Mary"
,
8881234567
)
bob_account
.
withdraw
(
200
)
mary_account
.
deposit
(
200
)
bob_account
.
display
()
mary_account
.
display
()
Note that in both the method definition in Example 9-6 and in its use in the preceding code, empty parentheses are included. Such use is optional; however, we include it to reinforce the fact that parameters are needed.
At the end of executing those instructions, bob_account
would have $300 as its balance,
and mary_account
would have $700.
However, every time we would want to use the Account
class to transfer money, we would have
to write two lines: one for withdrawing from the old account and another
for depositing to a new one. It would be much easier to use the Account
class if the two functionalities were
combined into a single method. This single method would need to affect
two separate instances of a single class. This is done by passing an
account object to a new method called transfer
, shown in Example 9-7.
1
def
transfer
(
amount
,
target_account
)
2
@balance
-=
amount
3
target_account
.
deposit
(
amount
)
4
end
Finally, all our methods thus far affected values stored in the program.
However, none of our defined methods returned a value to the
invoking statement. That is, if one wished to assign the balance of an
account to a variable, this balance would need to be returned after a
sequence of deposits and withdrawals. To obtain this value, a method
must be defined that returns a value. We define such a method, called
status
,
as shown in Example 9-8.
Two items are critical to note about the definition of the
status
method. First, the return
construct returns the value of @balance
to the method-invoking element. For
the sophisticated Ruby programmer, the reality is that Ruby always
returns the value of the last statement executed. However, if a
different value or better clarity is desired, a return
statement is often used.
Second, since there is no local overriding parameter called
@balance
, the global value for
@balance
is accessed. Example 9-9 contains the full
implementation of our Account
class.
1
class
Account
2
def
initialize
(
balance
,
name
,
phone_number
)
3
@balance
=
balance
4
@name
=
name
5
@phone_number
=
phone_number
6
end
7
8
def
deposit
(
amount
)
9
@balance
+=
amount
10
end
11
12
def
withdraw
(
amount
)
13
@balance
-=
amount
14
end
15
16
def
display
17
puts
"Name: "
+
@name
18
puts
"Phone number: "
+
@phone_number
.
to_s
19
puts
"Balance: "
+
@balance
.
to_s
20
end
21
22
def
transfer
(
amount
,
target_account
)
23
@balance
-=
amount
24
target_account
.
deposit
(
amount
)
25
end
26
27
def
status
28
return
@balance
29
end
30
end
18.191.176.228