Question
Using the readings and videos from this week as your guide, create your own simple GUI. Provide the code, describe the functionality and demonstrate it
Using the readings and videos from this week as your guide, create your own simple GUI. Provide the code, describe the functionality and demonstrate it compiles and runs as designed.
The classes created in module 2 will now be used to create a simple application. This application is not complete but instead is used to show some of the principles involved in actually implementing a GUI application. The changes needed to the classes from module 2, as well as the actual implementation of the GUI, will be illustrated in the following sections.
I. Changes to the Bank class
The class that is most changed in this application is the Bank class. These changes are explained below.
Programs/bank/Bank.java
A. Using Maps to Store Data
The first change that was made to Bank was to take _accounts and _customers collections and change them to implement Java Maps. The reason for doing this is that these two structures are used to find an object, such as an Account or Customer, given a key (account number or name). This change not only allows the data to be found by a key value, but also allows the keys to be the only immutable part of the data object.
In this case, both of these changes are implemented using HashMaps, but as is good practice, the actual type of Map that is used is specified only as a Map, which allows it to be changed later if the need arises (for example, if a sorted list of clients is needed). The key for the Accounts is an Integer, and so autoboxing is used in the application. The key for the Customer is the customer name, so the Name class was changed to make it immutable. The new Name class is referenced below. Note the use of the final modifier in the name class for the String variables _firstName and _lastName. It should be obvious that final in Java is not used simply to define constants.
In this case, the final modifier simply says that the value must be set before the object is finished being constructed, so we can set the actual values in the constructor. After the object is constructed, the variables cannot be changed. The fact that the string itself is immutable ensures that these variables cannot be changed, and thus a Name object is immutable.
Programs/bank/Name.java
Even though it is immutable, whether it was a good decision to make the key to the Customer the Name is explored in the self-assessment questions.
B. The Ability to Save and Restore the Bank Was Added
The next major change to the program was that the ability to save and restore the bank was added. Saving the Bank is accomplished by writing the _accounts and _customers Maps to an ObjectOutputStream. Note that both streams can be written to the same file, as can the integer variable used to determine the account number for the next account. The Bank can then be reconstituted by simply reading these two Map objects using an ObjectInputStream. This example shows the power of ObjectStreams.
Note that in module 1 we wrote the Account class so that it would keep track of the _nextAccountNumber in a static variable inside the Account class itself. This worked well because it did not need to be visible outside of the Account class. However, now when a Bank is saved and restored, we must read, and worse, assign this number. So methods were added to the Account class to manipulate the _nextAccountNumber. The effect of this decision is explored in the self-assessment questions.
Serializing classes
Because ObjectStreams are used to save the data, all of the classes for objects that are to be written to the ObjectStream must be declared to implement Serializable. This declaration is generally needed for all classes, and simply involves having these classes implement Serializable.
Note that when these classes are compiled, a compiler warning is generated that the variable serialVersionUID is not declared. This warning was left in most of the classes to illustrate what happens when it is left out; however, it was fixed in the BlueCustomer class to show how to fix this warning. The BlueCustomer class is referenced below.
Programs/bank/BlueCustomer.java
The reason for this warning is that if ever a class is changed to add new data items to the class, objects that were written with a previous version of this class will no longer be valid. If all that is changed, however, is the functionality of a method, then the objects created with the old version of this class are still valid with the new version. The serialVersionUID variable is used to give the programmer control over determining if an object that was generated and stored to an ObjectStream is still valid to be used with a new version of a class. If the serialVersionUID is set, it is up to the programmer to ensure that the class is still valid or that the variable is updated. If the variable is not set, the compiler generates a new random number each time the class is compiled. If the serialVersionUID does not match between the object written to the ObjectStream and the current version of the class, an exception is thrown when the object is read.
A complete new set of the files needed for this system is given below. Note that generally the changes have been limited to adding the implement Serializable clause to each class.
Programs/bank/Account.java Programs/bank/CheckingAccount.java Programs/bank/SavingsAccount.java Programs/bank/Customer.java Programs/bank/GoldCustomer.java Programs/bank/SilverCustomer.java Programs/bank/BankException.java Programs/bank/AccountAccessException.java Programs/bank/CustomerAccessException.java Programs/bank/InsufficentFundsException.java
The Unit Testing Program
Note that the unit testing program, TestBank.java, which was used in module 1, is invaluable as we make changes to the Bank to test new features as they are added. Too often new features break old ones, so unit tests for a class are often considered as important as the class itself. In this module, unit tests are written by hand. However, in industry, there are a number of unit testing tools. The two best in the Java world, JUnit and TestNG, are both free.
Always remember to keep your unit tests when developing a system and to keep them up to date. They can save countless hours of debugging of the overall system when a unit- level problem is introduced.
One interesting problem in the testing was that to be able to test the save and restore methods, we had the Bank return a Collection to the _customers and _accounts Maps. Whether or not this was a good decision will be the subject of a discussion in module 4.
Programs/TestBank.java
II. Writing the GUI
Finally, we are ready to write the GUI. Note that the GUI includes the JList, JScrollPane, and JPanel that were discussed earlier in this module.
The most important feature to be considered in the GUI, however, is that no application logic is present. Everything in the GUI is about retrieving and displaying the data on the forms. All the application logic is performed through calls to the Bank application. This is the crux of "n-tiered" architectures, the most common of which is called a three-tiered architecture.
In three-tiered architecture, there are three separate layers to the application. The outermost layer, the presentation layer, should be concerned only with formatting, displaying, and inputting data. No application logic should exist in this layer. The middle layer(s) is where the business logic should reside. Finally, the innermost layer should be concerned with the data, such as databases.
These layers access each other only through messages that are passed between the tiers. Each layer in the application should know nothing about the other layers except how to send and receive messages from them. This isolation allows each layer to concentrate on specific issues for that layer. This type of architecture allows the application to change more easily, because changes are more isolated. It also improves security, because only the relevant information for a layer is available. For example, the presentation layer cannot directly access the data layer, which allows the data layer to be hidden from the end user.
Note that our application is really only two-tiered, because there is no data access layer and all data is kept in the _accounts and _customers maps. In a business application, these data should be moved out of the middle tier. But the concept of not allowing business logic access, or much worse, actual access to the data, in the presentation layer is something that should be observed.
There is one more potential situation to consider. To display the accounts owned by a customer in the GUI, a reference to the Collection for the account for a Customerwas actually passed to the presentation layer using the getAccounts() method. This passing is similar to the returning of Collections referring to the _customers and _accounts for the Bank discussed in the previous section. Once again, this issue will be discussed in module 4.
Programs/BankApplication.java
The application is now complete. To compile the application, type "javac BankApplication.java", and to run it, type "java BankApplication".
Make sure when you compile these programs that the "bank" package is in your CLASSPATH, and that you compile from a separate directory that contains the TestBank and BankApplication. Please note that you must correctly put the files for the "bank" package in a separate directory named "bank", and this directory must be different from the files TestBank and BankApplication.
import javax.swing.*;
import java.awt.event.*;
import bank.*;
import java.util.GregorianCalendar;
import java.io.IOException;
import java.awt.Container;
import java.awt.GridLayout;
import java.awt.BorderLayout;
public class BankApplication {
public static void main(String[] args) {
final Bank bank = Bank.getBank();
final JFrame jf = new JFrame();
Container con = jf.getContentPane();
con.setLayout(new GridLayout(5,1));
final JDialog customerDialog = createCustomerDialog(jf);
JButton button = new JButton("Customer Options");
button.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent ae) {
customerDialog.pack();
customerDialog.setVisible(true);
}
});
con.add(button);
final JDialog accountDialog = createAccountDialog(jf);
button = new JButton("Account Options");
button.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent ae) {
accountDialog.pack();
accountDialog.setVisible(true);
}
});
con.add(button);
button = new JButton("Save Bank");
button.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent ae) {
try {
JFileChooser chooser = new JFileChooser();
//In response to a button click:
int returnVal = chooser.showSaveDialog(jf);
if(returnVal == JFileChooser.APPROVE_OPTION) {
bank.saveBank(chooser.getSelectedFile().getName());
}
} catch (Exception ie) {
ie.printStackTrace();
}
}
});
con.add(button);
button = new JButton("Restore Bank");
button.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent ae) {
try {
JFileChooser chooser = new JFileChooser();
//In response to a button click:
int returnVal = chooser.showOpenDialog(jf);
if(returnVal == JFileChooser.APPROVE_OPTION) {
bank.restoreBank(chooser.getSelectedFile().getName());
}
} catch (Exception ie) {
ie.printStackTrace();
}
}
});
con.add(button);
button = new JButton("Exit");
button.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent ae) {
System.exit(0);
}
});
con.add(button);
jf.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
jf.pack();
jf.setVisible(true);
}
/**
This method creates the dialog for the customer
*/
public static JDialog createCustomerDialog(JFrame parent) {
final JDialog dialog = new JDialog(parent, "Customer", true);
Container con = dialog.getContentPane();
JPanel dataPanel = new JPanel();
dataPanel.setLayout(new GridLayout(4,2));
dataPanel.add(new JLabel("First Name"));
final JTextField firstName = new JTextField();
dataPanel.add(firstName);
dataPanel.add(new JLabel("Last Name"));
final JTextField lastName = new JTextField();
dataPanel.add(lastName);
dataPanel.add(new JLabel("Date of Birth"));
final JTextField dob = new JTextField();
dataPanel.add(dob);
dataPanel.add(new JLabel("Customer Type"));
String[] custStrings = {"Blue Customer", "Silver Customer",
"Gold Customer"};
final JComboBox custType = new JComboBox(custStrings);
custType.setEditable(false);
dataPanel.add(custType);
con.add(dataPanel, BorderLayout.NORTH);
JPanel accountPanel = new JPanel();
accountPanel.add(new JLabel("Accounts"));
final JList accounts = new JList(new DefaultListModel());
JScrollPane jsp = new JScrollPane(accounts);
accountPanel.add(jsp);
con.add(accountPanel, BorderLayout.CENTER);
JPanel buttonPanel = new JPanel();
JButton button = new JButton("Add");
buttonPanel.add(button);
button.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent ae) {
Name name = new Name(firstName.getText(),
lastName.getText());
Customer cust = Bank.getBank().getCustomer(name);
if (cust != null) {
JOptionPane.showMessageDialog(null, "Customer already exists");
return;
}
// Note: it is too much trouble to format the name,
// and we already showed how to sort with it, so just
// set it to a constant.
try {
if (custType.getSelectedIndex() == 2)
Bank.getBank().addCustomer(new GoldCustomer(
name, new GregorianCalendar(1985, 1, 1), 1000.00f));
else if (custType.getSelectedIndex() == 1)
Bank.getBank().addCustomer(new SilverCustomer(
name, new GregorianCalendar(1985, 1, 1), 1000.00f));
else
Bank.getBank().addCustomer(new BlueCustomer(
name, new GregorianCalendar(1985, 1, 1)));
} catch (CustomerAccessException cae) {
JOptionPane.showMessageDialog(null, "Customer already exists");
return;
}
}
});
button = new JButton("Find");
button.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent ae) {
Name name = new Name(firstName.getText(),
lastName.getText());
Customer cust = Bank.getBank().getCustomer(name);
if (cust == null) {
JOptionPane.showMessageDialog(null, "Customer not found");
return;
}
firstName.setText(cust.getName().getFirstName());
lastName.setText(cust.getName().getLastName());
dob.setText(cust.getDateOfBirth());
if (cust instanceof GoldCustomer)
custType.setSelectedIndex(2);
else if (cust instanceof SilverCustomer)
custType.setSelectedIndex(1);
else
custType.setSelectedIndex(0);
DefaultListModel lm = (DefaultListModel)(accounts.getModel());
lm.clear();
for (Account a : cust.getAccounts())
lm.addElement(a);
}
});
buttonPanel.add(button );
button = new JButton("Change");
buttonPanel.add(button);
button.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent ae) {
Name name = new Name(firstName.getText(),
lastName.getText());
Customer cust = Bank.getBank().getCustomer(name);
if (cust == null) {
JOptionPane.showMessageDialog(null, "Customer must exist to change them");
return;
}
// Note: it is too much trouble to format the name,
// and we already showed how to sort with it, so just
// set it to a constant.
try {
if (custType.getSelectedIndex() == 2)
Bank.getBank().changeCustomerType(
new GoldCustomer(
name, new GregorianCalendar(1985, 1, 1), 1000.00f));
else if (custType.getSelectedIndex() == 1)
Bank.getBank().changeCustomerType(
new SilverCustomer(
name, new GregorianCalendar(1985, 1, 1), 1000.00f));
else
Bank.getBank().changeCustomerType(
new BlueCustomer(
name, new GregorianCalendar(1985, 1, 1)));
} catch (CustomerAccessException cae) {
JOptionPane.showMessageDialog(null, "Customer must exist to change them");
return;
}
}
});
buttonPanel.add(button );
button = new JButton("Clear");
button.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent ae) {
firstName.setText("");
lastName.setText("");
dob.setText("");
((DefaultListModel)(accounts.getModel())).clear();
}
});
buttonPanel.add(button );
button = new JButton("Done");
button.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent ae) {
dialog.setVisible(false);
}
});
buttonPanel.add(button );
dialog.add(buttonPanel, BorderLayout.SOUTH);
dialog.setLocation(125,0);
return dialog;
}
/**
* This method creates the dialog for the account.
*/
public static JDialog createAccountDialog(JFrame parent) {
final JDialog dialog = new JDialog(parent, "Customer", true);
Container con = dialog.getContentPane();
JPanel dataPanel = new JPanel();
dataPanel.setLayout(new GridLayout(4,2));
dataPanel.add(new JLabel("Account Number"));
final JTextField accountNumber = new JTextField();
dataPanel.add(accountNumber);
dataPanel.add(new JLabel("Balance"));
final JTextField balance = new JTextField();
dataPanel.add(balance);
dataPanel.add(new JLabel("Customer Name"));
final JTextField name = new JTextField();
dataPanel.add(name);
dataPanel.add(new JLabel("Customer Type"));
String[] custStrings = {"Blue Customer", "Silver Customer",
"Gold Customer"};
final JComboBox custType = new JComboBox(custStrings);
custType.setEditable(false);
dataPanel.add(custType);
con.add(dataPanel, BorderLayout.NORTH);
JPanel buttonPanel = new JPanel();
JButton button;
button = new JButton("Find");
buttonPanel.add(button);
button.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent ae) {
int acctNo = Integer.parseInt(accountNumber.getText());
Account acct = Bank.getBank().getAccount(acctNo);
if (acct == null) {
JOptionPane.showMessageDialog(null, "Account not found");
return;
}
accountNumber.setText("" + acct.getAccountNumber());
balance.setText("" + acct.getBalance());
Customer cust = acct.getAccountOwner();
name.setText(cust.getName().toString());
if (cust instanceof GoldCustomer)
custType.setSelectedIndex(2);
else if (cust instanceof SilverCustomer)
custType.setSelectedIndex(1);
else
custType.setSelectedIndex(0);
}
});
buttonPanel.add(button );
button = new JButton("Add");
buttonPanel.add(button);
button.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent ae) {
JOptionPane.showMessageDialog(null, "Option not implemented");
return;
}
});
button = new JButton("Done");
button.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent ae) {
dialog.setVisible(false);
}
});
buttonPanel.add(button );
dialog.add(buttonPanel, BorderLayout.SOUTH);
dialog.setLocation(125,0);
return dialog;
}
}
Step by Step Solution
There are 3 Steps involved in it
Step: 1
Get Instant Access to Expert-Tailored Solutions
See step-by-step solutions with expert insights and AI powered tools for academic success
Step: 2
Step: 3
Ace Your Homework with AI
Get the answers you need in no time with our AI-driven, step-by-step assistance
Get Started