P04 EXCEPTIONAL BANKING
- This assignment is due by 10:00PM on Thursday, October 4th.
- Pair Programming is NOT allowed for this assignment. Every student must complete this assignment on their own, and submit their unique solution to their own zybooks account.
As you have been working on the JunglePark and AccessControl programs, other programmers were assigned to refactor your AuditableBanking code from P1 into a more object oriented organization. In this assignment, you will need to familiarize yourself with their code so that you can enhance it with exception handling mechanisms. Your solution for this assignment will not constitute a complete application with driver, but it will instead be an enhanced version of the provided code, along with several tests to demonstrate the correctness of this solution.
OBJECTIVES AND GRADING CRITERIA
A goal of this assignment includes developing your ability to read and understand object oriented code that is written by others. You’ll also develop your understanding of the difference between checked and unchecked exceptions, and get practice both throwing and catching exceptions of each kind. Finally, you will get more practice writing tests, specifically tests that detect whether exceptions are thrown under the prescribed circumstances or not.
20 points | 4 zyBooks Tests: these automated grading test results are visible upon submission, and allow multiple opportunities to correct the organization and functionality of your code. Only your highest scoring submission prior to the deadline will be recorded. |
20 points | 4 Final Tests: these automated grading tests are run after the assignment’s deadline. They check for similar functionality and organizational correctness as the zyBooks tests. However you will NOT be able to resubmit corrections for extra points, and should therefore consider and test the correctness of your own code as thoroughly as possible. |
10 points | Code Readability: human graders will review the commenting, style, and organization of your final submission. They will be checking whether it conforms to the requirements of the CS300 Course Style Guide. You will NOT be able to resubmit corrections for extra points, and should therefore carefully review the readability of your code with respect to the course style guide. |
SUBMISSION
For this assignment, you will need to submit three files through zybooks: TransactionGroup.java, Account.java, and ExceptionalBankingTests.java.
STEP1. FAMILIARIZING YOURSELF WITH EXCEPTIONAL BANKING
Start by downloading these two files: TransactionGroup.java and Account.java. Add these files to a new project, and then create and add a third file named ExceptionalBankingTests.java to the same project. To help familiarize yourself with this code, carefully read through it and add comments as you go.
Start with the TransactionGroup class. Notice how this implementation provides a general interface for accessing the individual transactions within a group, and that these transactions can be accessed in an encoding agnostic manner. Pay close attention to how the Binary Amount transactions are counted and handled, since it has changed slightly since P1. Consecutive zeros or ones within the same transaction group are counted as a single transaction instead of being counted as several. You don’t need to make any changes to this computation, but make sure that you are clear about what it is doing and how.
Next read through and comment the Account class in the same way.
1
2
|
public static boolean testAccountBalance() { }
public static boolean testOverdraftCount() { }
|
Now create a couple of tests within ExceptionalBankingTests.java to help verify your understanding of how this code works. These tests should be static methods named “testAccountBalance” and “testOverdraftCount” and they should each return a primitive boolean value to that indicate whether the code they are testing has passed the test (true) versus failed for any reason (false). Each test method should create a new Account, add transaction groups of each type to that account, and then verify that the resulting balance or overdraft count for that object match your expectations. These tests should focus on checking the correct functionality resulting from valid inputs or command sequences. Your testOverdraftCount method should specifically confirm your understanding of how Binary Amount transaction groups are now counted differently than they were in P1.
STEP2. TRANSACTION GROUP EXCEPTIONS
Whenever a new transaction group is created, there is some potential for trouble. One kind of trouble that we might encounter is that the int[] passed into this class’ constructor may contain data that does not conform to any of the three encoding types described in the P1 write-up. In fact these specific encoding requirements that we are currently checking for might change in the future: new encoding types might be added, and existing types might be removed or otherwise changed. Therefore we’d like to make sure that any programmers using our TransactionGroup class know about this potential problem, and have a plan in place for how their code responds to such problems. What kind of exception does this sound like a good opportunity to use: A) a checked exception type, or B) an unchecked exception type? Think through your answer to this question before moving on.
Review the descriptions of encoding errors described in the P1 write-up (under step 2). If any of these kinds of errors are detected in the int[] passed into the TransactionGroup’s constructor, the constructor should throw a DataFormatException (import from java.util.zip.DataFormatException in the Java API to use). Here are the exact messages that you should include within these DataFormatExceptions to provide more specific details about the kinds of problems that lead to their being created and thrown:
- transaction group encoding cannot be null or empty
- the first element within a transaction group must be 0, 1, or 2
- binary amount transaction groups may only contain 0s and 1s
- integer amount transaction groups may not contain 0s
- quick withdraw transaction groups must contain 5 elements
- quick withdraw transaction groups may not contain negative numbers
You can see in the JavaAPI for DataFormatException that this class extends Exception, and is therefore an example of a checked exception type. This means that we must update the signature of our constructor to declare that it occasionally throws this type of checked exception. This change will create some errors within the Account class, and we’ll get to fixing those shortly. But first, there is one more exception that we’ll be throwing from the TransactionGroup class.
Another potential problem that we want to address with Exceptions is in the getTransactionAmount method. See any potential problems here? This method should throw an IndexOutOfBoundsException whenever the index passed into it does not fall within the range of valid indexes. The message within this exception should include both the value of the index being accessed, and the total number of transactions within the group that it is being called on. The specific wording is up to you, but you can try causing an exception like this to be thrown from an ArrayList to see how it’s IndexOutOfBoundsException message is formatted.
You can see in the JavaAPI for IndexOutOfBoundsException that this class extends RuntimeException, and is therefore an example of an unchecked exception type. Why should we use an unchecked exception in this case, instead of a checked exception like we used previously? There is some controversy around the use of checked and unchecked exceptions. One way of understanding our preference for an unchecked exception here is to consider the frequency that Java programmers use indexes. We expect competent programmers to be familiar with the concept of indexes needing to lie within a valid range, and to write their code in a careful manner that avoids index out of bounds problems. When these problems arise, the solution is usually to fix the code to avoid ever throwing them in the first place, rather than to add additional code that responds to such problems. It would also be a heavy burden to require programmers to explicitly catch or throw such exceptions, since they are possible from so many places within our programs (almost as common as NullPointerExceptions). For all of these reasons, we have chosen to go with an unchecked exception in this case. It is still a good idea to declare that this method occasionally throws this type of unchecked exception within the method’s signature. Be sure to include descriptions of when your methods throw exceptions of different types using the @throws tag within the JavaDoc style headers for each method.
STEP3. ACCOUNT EXCEPTIONS
Since the TransactionGroup’s constructor occasionally throws a checked exception, we must deal with this in some way in Account’s addTransactionGroup method. Since we don’t have a good general way to correct the problem here (at this level of abstraction), we’ll have this constructor itself throw any DataFormatExceptions that are thrown from within its internal method calls. This will “pass the buck”, leaving the responsibility for reacting to such problems to the code that is attempting to create new objects using this constructor.
The addTransactionGroup method should throw new DataFormatExceptions with the following exact message, when the string parameter passed into it does not contain a space separated sequence of values that can be interpreted as integers.
- addTransactionGroup requires string commands that contain only space separated integer values
We’ll also have this addTransactionGroup method occasionally throw an Error, which like RuntimeExceptions do not need to be explicitly caught or thrown (they are considered unchecked). When this method is called and the oversize array is already full, this method should throw a new OutOfMemoryError with a message that includes the capacity of this Account object’s internal array storage. Although Errors usually indicate problems that cannot be recovered from, this will be our way of making it clear that the capacity of these accounts is a very hard limit.
The getTransactionAmount() method in our Account class takes an index which is at risk of being out of bounds (similar to the method of the same name within our TransactionGroup class). Detect and respond to this kind of problem, in the same way that we responded to it from our TransactionGroup class. Ensure that the exception message includes both the (bad) index that is being accessed, and the total number of accessible transactions within the account.
STEP4. LOAD ACCOUNT FROM FILE
The Account class includes a constructor that takes a file object as input. And that the provided implementation ignores this file and instead reads from System.in. Change this implementation to read from the file instead of from System.in.
The only type of checked exception that should ever be thrown from this constructor is a FileNotFoundException. And your method should be throwing exactly the same exception object, as was created and thrown from the Scanner’s constructor when attempting to open a file that is not accessible (when it does not exist, for example).
This means that other kinds of checked exceptions that can possibly be thrown from the statements within this constructor’s definition must be caught, to prevented them from being thrown any further. For example, whenever a call to addTransactionGroup results in a DataFormatException being thrown: 1) catch that exception, 2) print a warning message to System.out that includes the message from within the caught exception, and then 3) attempt to finish reading the rest of the transaction groups from that file as usual. At the end of this process, your account should contain transaction groups corresponding to each of the lines that are correctly formatted in the file.
Notice that there are some opportunities for unchecked exceptions to be thrown here: if a unique number is not in integer form, if the file is empty or too short, etc. Instead of catching these unchecked exceptions, we will simply allow them to be thrown from the constructor.
Do make sure that the scanner reading from this file is always closed: whether any exceptions are thrown or caught or not!
STEP5. TESTING THESE METHODS
Now that our two classes are able to respond to a variety of different kinds of problems, let’s write some tests to help convince ourselves that this code is working in the intended way. First revisit your tests from Step1, and update them. If any exceptions are thrown from the Account or TransactionGroup classes, these tests should fail.
Then add the following test methods to your ExceptionalBankingTests class. Implement each of these tests to demonstrate that exceptions are only thrown as a result of the specific triggers demonstrated in this write-up.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
|
/**
* This method checks whether the TransactionGroup constructor throws an exception with an
* appropriate message, when it is passed an empty int[].
* @return true when test verifies correct functionality, and false otherwise.
*/
public static boolean testTransactionGroupEmpty() { return false; }
/**
* This method checks whether the TransactionGroup constructor throws an exception with an
* appropriate message, when then first int in the provided array is not 0, 1, or 2.
* @return true when test verifies correct functionality, and false otherwise.
*/
public static boolean testTransactionGroupInvalidEncoding() { return false; }
/**
* This method checks whether the Account.addTransactionGroup method throws an exception with an
* appropriate message, when it is passed a quick withdraw encoded group that contains negative
* numbers of withdraws.
* @return true when test verifies correct functionality, and false otherwise.
*/
public static boolean testAccountAddNegativeQuickWithdraw() { return false; }
/**
* This method checks whether the Account.addTransactionGroup method throws an exception with an
* appropriate message, when it is passed a string with space separated English words (non-int).
* @return true when test verifies correct functionality, and false otherwise.
*/
public static boolean testAccountBadTransactionGroup() { return false; }
/**
* This method checks whether the Account.getTransactionAmount method throws an exception with an
* appropriate message, when it is passed an index that is out of bounds.
* @return true when test verifies correct functionality, and false otherwise.
*/
public static boolean testAccountIndexOutOfBounds() { return false; }
/**
* This method checks whether the Account constructor that takes a File parameter throws an
* exception with an appropriate message, when it is passed a File object that does not correspond
* to an actual file within the file system.
* @return true when test verifies correct functionality, and false otherwise.
*/
public static boolean testAccountMissingFile() { return false; }
|
Although we are not requiring you to write additional test methods beyond those listed above, this can be a helpful way to find bugs in the Account and TransactionGroup classes. For this reason, writing additional tests is highly recommended.
SUBMISSION
Congratulations on finishing this CS300 assignment! After verifying that your work is correct, and written clearly in a style that is consistent with the course style guide, you should submit your final work through zybooks. The most recent of your highest scoring submissions prior to the deadline of 10:00PM on Thursday, October 4th will be recorded as your zybooks test score. The other portions of your grade for this assignment will be assessed on this same submission. |