9.3 Data and Methods

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).

9.3.1 Grouping Data and 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.

Example 9-3. Account version 2
    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

Gem of Wisdom

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.

Example 9-4. Account version 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 
     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.

9.3.2 Implementing 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.

Example 9-5. Account version 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 		@balance += amount
    10 	end
    11 
    12 	def withdraw(amount)
    13 		@balance -= amount
    14 	end
    15 end

Gem of Wisdom

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.

Example 9-6. Display method
    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.

Example 9-7. Transfer method
    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.

Example 9-8. Status method
    1 def status
    2   return @balance
    3 end

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.

Example 9-9. Account—final version (version 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 
    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
..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.191.176.228