4.9 Account Class with a Balance; Processing Monetary Amounts

In this section, we’ll declare an Account class that maintains an Account’s balance in addition to its name. Most account balances are not whole numbers (such as 0, –22 and 1024), rather they’re numbers that include a decimal point, such as 99.99 or –20.15. For this reason, class Account represents the account balance using type decimal, which is designed to precisely represent numbers with decimal points, especially monetary amounts.

4.9.1 Account Class with a decimal balance Instance Variable

A typical bank services many accounts, each with its own balance, so our next Account class (Fig. 4.11) maintains a bank account’s

  • name—as the auto-implemented Name property (line 6)—and

  • balance—as the private decimal instance variable balance (line 7) and a corresponding public Balance property (lines 17–32). We use a fully implemented Balance property here so we can ensure that the set accessor’s argument is valid before assigning it to the balance instance variable.

A decimal instance variable is initialized to zero by default. Every instance (i.e., object) of class Account contains its own name and balance.

Fig. 4.11 Account class with a decimal instance variable balance and a Balance property and Deposit method that each perform validation.

Alternate View

  1    // Fig. 4.13: Account.cs
  2    // Account class with a balance and a Deposit method.
  3
  4    class Account
  5    {
  6       public string Name { get; set; } // auto-implemented property
  7       private decimal balance; // instance variable
  8
  9       // Account constructor that receives two parameters
 10       public Account(string accountName, decimal initialBalance)
 11       {
 12          Name = accountName;
 13          Balance = initialBalance; // Balance's set accessor validates
 14       }
 15
 16       // Balance property with validation
 17       public decimal Balance
 18       {
 19          get
 20          {
 21             return balance;
 22          }
 23          private set // can be used only within the class                 
 24          {                                                                
 25             // validate that the balance is greater than 0.0; if it's not,
 26             // instance variable balance keeps its prior value            
 27             if (value > 0.0m) // m indicates that 0.0 is a decimal literal
 28             {                                                             
 29                balance = value;                                           
 30             }                                                             
 31          }                                                                
 32      }
 33
 34      // method that deposits (adds) only a valid amount to the balance
 35      public void Deposit(decimal depositAmount)                       
 36      {                                                                
 37         if (depositAmount > 0.0m) // if the depositAmount is valid    
 38         {                                                             
 39            Balance = Balance + depositAmount; // add it to the balance
 40         }                                                             
 41      }                                                                
 42    }

Account Class Two-Parameter Constructor

It’s common for someone opening an account to deposit money immediately, so the constructor (lines 10–14) now receives a second parameter—initialBalance of type decimal that represents the starting balance. Line 13 assigns initialBalance to the property Balance, invoking Balance’s set accessor to ensure that the initialBalance argument is valid before assigning a value to the instance variable balance.

Account Property Balance

Property Balance (lines 17–32) of type decimal provides a get accessor, which allows clients of the class to obtain a particular Account object’s balance. The property also provides a set accessor.

In Fig. 4.6, class Account defined a Name property in which the set accessor simply assigned the value received in its implicit parameter value to class Account’s instance variable name. The Name property did not ensure that name contains only valid data.

The Balance property’s set accessor performs validation (also known as validity checking). Line 27 (Fig. 4.11) ensures that the set accessor’s implicit value parameter is greater than 0.0m—the letter m (or M) indicates that 0.0 is a decimal literal.4 If value is greater than 0.0m, the amount stored in value is assigned to instance variable balance (line 29). Otherwise, balance is left unchanged—we’ll say more about error processing throughout the book.

Though we validated the balance in this example, we did not validate the name. Names are normally quite “free form”—there’s a wide variety of acceptable name formats. Often when you fill out a form, you’re asked to limit a name to a certain number of characters. In Chapter 16, Strings and Characters: A Deeper Look, you’ll learn how to check a string’s length, so that you can validate strings by checking that they’re not too long.

set and get Accessors with Different Access Modifiers

By default, a property’s get and set accessors have the same access as the property—e.g., public property Name’s accessors are public. It’s possible to declare the get and set accessors with different access modifiers. In this case, one of the accessors must implicitly have the same access as the property and the other must be explicitly declared with a more restrictive access modifier than the property. We declared the Balance property’s set accessor private—this indicates that it may be used only in class Account, not by the class’s clients. This enables us to ensure that once an Account object exists, its balance can be modified only by method Deposit (and in the exercises, by method Withdraw as well).

Error-Prevention Tip 4.3

The benefits of data integrity are not automatic simply because instance variables are made private —you must provide appropriate validity checking and report the errors.

Error-Prevention Tip 4.4

set accessors that set the values of private data should verify that the intended new values are proper; if they’re not, the set accessors should leave the instance variables unchanged and indicate an error. We demonstrate how to indicate errors by throwing exceptions in Chapter 10.

Account Class Deposit Method

The public method Deposit (lines 35–41) enables the client code to deposit money into an Account, thus increasing its balance. Line 35 indicates that

  • the method does not return any information to its caller (as indicated by the return type void) and

  • receives one parameter named depositAmount—a decimal value.

The depositAmount is added to the balance only if the parameter’s value is valid—that is, it’s greater than 0.0m as specified in line 37. Line 39 first adds the current Balance and depositAmount, forming a temporary sum which is then assigned to the Balance property. The property’s set accessor then replaces instance variable balance’s prior value (recall that addition has a higher precedence than the assignment operator). So line 39 uses the Balance property’s get accessor on the right side of the assignment and the set accessor on the left side. It’s important to understand that the calculation on the right side of the assignment operator in line 39 does not modify the instance variable balance—that’s why the assignment is necessary.

Software Engineering Observation 4.5

When implementing a method of a class, although it’s possible for the method to access the class’s instance variables directly, always use the class’s properties for that purpose. In Chapter 10, we’ll take a deeper look at this issue.

4.9.2 AccountTest Class That Uses Account Objects with Balances

Class AccountTest (Fig. 4.12) creates two Account objects (lines 9–10) and initializes them with a valid balance of 50.00m and an invalid balance of -7.53m, respectively—for the purpose of our examples, we assume that balances must be greater than or equal to zero. The calls to method Console.WriteLine in lines 13–16 output the account names and initial balances, which are obtained from each Account’s Name and Balance properties.

Fig. 4.12 Reading and writing monetary amounts with Account objects.

Alternate View

  1   // Fig. 4.12: AccountTest.cs
  2   // Reading and writing monetary amounts with Account objects.
  3   using System;
  4
  5   class AccountTest
  6   {
  7      static void Main()
  8      {
  9         Account account1 = new Account("Jane Green", 50.00m);
 10         Account account2 = new Account("John Blue", -7.53m); 
 11
 12         // display initial balance of each object
 13         Console.WriteLine(
 14            $"{account1.Name}'s balance: {account1.Balance:C}");
 15         Console.WriteLine(
 16            $"{account2.Name}'s balance: {account2.Balance:C}");
 17
 18         // prompt for then read input
 19         Console.Write("
Enter deposit amount for account1: ");
 20         decimal depositAmount = decimal.Parse(Console.ReadLine());
 21         Console.WriteLine(
 22            $"adding {depositAmount:C} to account1 balance
");
 23         account1.Deposit(depositAmount); // add to account1's balance
 24
 25         // display balances
 26         Console.WriteLine(
 27            $"{account1.Name}'s balance: {account1.Balance:C}");
 28         Console.WriteLine(
 29            $"{account2.Name}'s balance: {account2.Balance:C}");
 30
 31         // prompt for then read input
 32         Console.Write("
Enter deposit amount for account2: ");
 33         depositAmount = decimal.Parse(Console.ReadLine());
 34         Console.WriteLine(
 35            $"adding {depositAmount:C} to account2 balance
");
 36         account2.Deposit(depositAmount); // add to account2's balance
 37
 38         // display balances
 39         Console.WriteLine(
 40            $"{account1.Name}'s balance: {account1.Balance:C}");
 41         Console.WriteLine(
 42            $"{account2.Name}'s balance: {account2.Balance:C}");
 43      }
 44   }

Jane Green's balance: $50.00
John Blue's balance: $0.00

Enter deposit amount for account1: 25.53
adding $25.53 to account1 balance

Jane Green's balance: $75.53
John Blue's balance: $0.00

Enter deposit amount for account2: 123.45
adding $123.45 to account2 balance

Jane Green's balance: $75.53
John Blue's balance: $123.45

Displaying the Account Objects’ Initial Balances

When line 14 accesses account1’s Balance property, the value of account1’s balance instance variable is returned from line 21 of Fig. 4.11 and inserted into the interpolated string at line 14 of Fig. 4.12 for display. Similarly, when line 16 accesses account2’s Balance property, the value of account2’s balance instance variable is returned from line 21 of Fig. 4.11 and inserted into the interpolated string at line 16 of Fig. 4.12 for display. The balance of account2 is initially 0.00, because the constructor rejected the attempt to start account2 with a negative balance, so the balance instance variable retains its default initial value.

string Interpolation Expressions with Formatting

6 This app displays each Account’s balance as a monetary amount. You can specify formatting in a C# 6 string interpolation expression by following the value in the braces with a colon and a format specifier. For example, in line 14, the interpolation expression


{account1.Balance:C}

uses the format specifier C to format account1.Balance as currency. The Windows culture settings on the user’s machine determine the format for displaying currency amounts, such as the commas vs. periods for separating thousands, millions, etc. For example,

  • 50 displays as $50.00 in the United States (U.S.), as 50,00€ ( for euros) in Germany and as ¥50 in Japan.

  • 4382.51 displays as $4,382.51 in the U.S., as 4.382,51 € in Germany and as ¥4,382 in Japan.

  • 1254827.40 displays as $1,254,827.40 in the U.S., as 1.254.827,40€ in Germany and as ¥1,254,827 in Japan.

Figure 4.13 lists additional format specifiers.

Fig. 4.13 string format specifiers.

Format specifier Description
C or c Formats the string as currency. Includes an appropriate currency symbol ($ in the U.S.) next to the number. Separates digits with an appropriate separator character (in the U.S. its a comma between every three digits for thousands, millions, etc.) and sets the number of decimal places to two by default.
D or d Formats the string as a whole number (integer types only).
N or n Formats the string with a thousands separator and a default of two decimal places.
E or e

Formats the number using scientific notation with a default of six decimal places.

F or f

Formats the string with a fixed number of decimal places (two by default).

G or g Formats the number normally with decimal places or using scientific notation, depending on context. If a format item does not contain a format specifier, format G is assumed implicitly.
X or x Formats the string as hexadecimal (base 16 numbers; we discuss these in the online Number Systems appendix).

Reading a decimal Value from the User

Line 19 (Fig. 4.12) prompts the user to enter a deposit amount for account1. Line 20 declares local variable depositAmount to store each deposit amount entered by the user. Unlike instance variables (such as name and balance in class Account), local variables (like depositAmount in Main) are not initialized by default, so they normally must be initialized explicitly. As you’ll learn momentarily, variable depositAmount’s initial value will be determined by the user’s input.

Common Programming Error 4.3

The C# compiler will issue the compilation error " Use of unassigned local variable 'variableName' " if you attempt to use the value of an uninitialized local variable—in the error message, variableName will be the actual variable name. This helps you avoid dangerous execution-time logic errors. It’s always better to get the errors out of your programs at compilation time rather than execution time.

Line 20 obtains the input from the user by calling the Console class’s ReadLine method, then passing the string entered by the user to type decimal’s Parse method, which returns the decimal value in this string—each simple type has a Parse method. Lines 21–22 display the deposit amount in currency format.

Making a Deposit

Line 23 calls object account1’s Deposit method and supplies depositAmount as the method’s argument. The method then adds the parameter’s value to the Balance property (line 39 of Fig. 4.11). Then lines 26–29 in Fig. 4.12 output the balances of both Accounts again to show that only account1’s balance instance variable changed.

Reading a decimal Value and Depositing into account2

Line 32 prompts the user to enter a deposit amount for account2. Line 33 obtains the input from the user by calling method Console.ReadLine and passing the return value to type decimal’s Parse method. Lines 34–35 display the deposit amount. Line 36 calls object account2’s Deposit method and supplies depositAmount as the method’s argument. Then, the method adds that value to account2’s Balance property. Finally, lines 39–42 output the Balances of both Accounts again to show that only account2’s balance instance variable changed.

Duplicated Code in Method Main

The six statements at lines 13–14, 15–16, 26–27, 28–29, 39–40 and 41–42 are almost identical—they each output an Account’s name and balance. They differ only in the name of the Account object—account1 or account2. Duplicate code like this can create code-maintenance problems when it needs to be updated—if six copies of the same code require an error correction or update, you must make that change six times, without making errors. Exercise 4.13 asks you to modify Fig. 4.12 to include a DisplayAccount method that takes as a parameter an Account object and outputs the object’s name and balance, and contains only one copy of the output code. You’ll then replace Main’s duplicated statements with six calls to DisplayAccount.

Software Engineering Observation 4.6

Replacing duplicated code with calls to a method that contains one copy of that code can reduce the size of your program and improve its maintainability.

UML Class Diagram for Class Account

The class diagram in Fig. 4.14 concisely models class Account of Fig. 4.11. The diagram models in its second compartment the public properties Name of type string and Balance of type decimal. Class Account’s constructor is modeled in the third compartment with parameters name of type string and initialBalance of type decimal. The class’s public Deposit operation also is modeled in the third compartment—Deposit has a depositAmount parameter of type decimal. Method Deposit does not return a value (because it returns void in C#), so the UML class diagram does not specify a return type for this operation.

Fig. 4.14 UML class diagram for Account class of Fig. 4.11.

..................Content has been hidden....................

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