Java - Fondamenti di programmazione C. Thomas Wu Copyright © 2009 - The McGraw-Hill Companies srl Paragrafo presentazione 8.7 8.7 L e asserzioni costituiscono un meccanismo che può essere sfruttato per rilevare errori logici nei programmi. L’idea è abbastanza semplice: in qualsiasi punto del programma può essere inserita un’istruzione assert, con la seguente sintassi: assert <espressione booleana> L’<espressione booleana> rappresenta la condizione che deve essere verificata quando l’esecuzione del programma è corretta. Quando l’istruzione viene eseguita, se tale espressione è valutata true l’esecuzione prosegue regolarmente, altrimenti viene sollevato un errore AssertionError. Questo paragrafo introduce gli studenti all’uso delle asserzioni. PARAGRAFO 8.7 Java - Fondamenti di programmazione C. Thomas Wu Copyright © 2009 - The McGraw-Hill Companies srl 8.7 Sample Development 469 Sample Development 8.7 Sample Development Keyless Entry System We will develop a program that simulates a secure keyless entry system for a dormitory. Inside the entrance hall of a dorm, there is an entry system where the dorm residents must enter their names, room numbers, and passwords. Upon entry of valid data, the system will unlock the inner door that leads to the dorm’s living quarters. To implement this program, two helper classes are provided. The Door class simulates unlocking of the inner door. The Dorm class manages resident information. An instance of the Dorm class is capable of adding and deleting resident information, reading and saving resident information from and to a file, and retrieving information if given the resident’s name. We can verify the validity of the entered data by checking them against the information kept by a Dorm object. We can turn our simulation program into a real one by replacing the Door class with a class that actually controls the door. Java provides a mechanism called Java Native Interface (JNI) which can be used to embed a link to a lowlevel device driver code, so calling the open method actually unlocks the door. Problem Statement Implement a sentry program that asks for three pieces of information: resident’s name, room number, and a password. A password is any sequence of characters ranging in length from 4 to 8 and is unique to an individual dorm resident. If everything matches, then the system unlocks and opens the door.We assume no two residents have the same name. Use the provided support classes Door and Dorm. Overall Plan To provide a complete system, we actually have to write two separate programs. The first one is the administrative module for adding, removing, and updating the resident information. The second is the user module that interacts with the residents. Figure 8.8 shows the program diagrams for the two modules. In this section, we implement the user module. The administrative module is left as an exercise. To begin our development effort, we must first find out the capabilities of the Dorm and Door classes. Also, for us to implement the class correctly, we need the specification of the Resident class. Java - Fondamenti di programmazione C. Thomas Wu Copyright © 2009 - The McGraw-Hill Companies srl 470 Chapter 8 Exceptions and Assertions 8.7 Sample Development—continued User module Dorm Resident Door Administrative module A helper class provided to us Dorm A class we implement Resident One or more classes we implement Figure 8.8 Program diagrams for the user and administrative modules. Notice the same Dorm and Resident classes are used in both programs. User and administrative modules will include one or more classes (at least one is programmer-defined). Resident The Resident class maintains information on individual dorm residents. We will be dealing with many instances of this class in the program. A password assigned to a resident must be a sequence of 4 to 8 characters. For this class to work properly with the Dorm class, the class must include these public methods: Public Methods of Resident public Resident( ) Default constructor that creates a Resident object with name = “unassigned”, room = “000”, and id = “@13&”. public Resident(String name, String room, String password) throws IllegalArgumentException Creates a Resident object with the passed values. IllegalArgumentException is thrown when the given password has less than four or more than eight characters. Java - Fondamenti di programmazione C. Thomas Wu Copyright © 2009 - The McGraw-Hill Companies srl 8.7 Sample Development 471 public void setName(String name) Assigns the name. public void setPassword(String id) throws IllegalArgumentException Assigns the password. IllegalArgumentException is thrown when the given password has less than four or more than eight characters. public void setRoom(String room) Assigns the room. public String getName( ) Returns the name. public String getPassWord( ) Returns the password. public String getRoom( ) Returns the room number. One important restriction to the Resident class is the requirement for the class to implement the Serializable interface. Because the Resident objects are saved to a file, Java requires the class definition to include the phrase implements Serializable as import java.io.*; class Resident implements Serializable { ... } Details on the significance of the clause implements Serializable will be given when we discuss the file input and output in Chapter 12. For any object we need to save to a file, its class definition must include the phrase implements Serializable. Dorm The Dorm class is a helper class provided to us. A Dorm object is capable of managing a list of Resident objects. It allows the client to add, delete, and retrieve Resident objects. In addition, it is capable of saving a list to a file or reading a list from a file. By Java - Fondamenti di programmazione C. Thomas Wu Copyright © 2009 - The McGraw-Hill Companies srl 472 Chapter 8 Exceptions and Assertions 8.7 Sample Development—continued having these file input and output features, our program can work with different lists of residents much as a word processor can work with different documents (files). The class definition is as follows: Public Methods of Dorm public Dorm( ) Default constructor that creates a Dorm object. public Dorm(String filename) Creates a Dorm object with the resident list read from the file with the name filename.Throws FileNotFoundException when the designated file cannot be found and IOException when the file cannot be read. public void openFile(String filename) Reads the resident list from the designated file.Throws FileNotFoundException when the designated file cannot be found and IOException when the file cannot be read. public void saveFile(String filename) Saves the resident list to the designated file. Throws IOException when the file cannot be saved. public void add(Resident resident) Adds the resident to the list. Throws IllegalArgumentException when a resident with the same name already exists in the list. We do not allow duplicate names. Every resident must have a unique name. public void delete(String name) Deletes the designated resident from the list. If no such resident is in the list, nothing happens. public Resident getResident(String name) Returns the Resident object with the given name. Returns null if no matching Resident is found. public String getResidentList( ) Returns a list of residents as a String. A line separator is used after each resident. For each resident, the list contains his or her name, room number, and password. Door The Door class is another helper class. It simulates the opening of the door. In a real control program, a Door object can have an embedded low-level device driver code, wu23399_ch08.qxd 12/15/06 19:58 Page 473 Java - Fondamenti di programmazione C. Thomas Wu Copyright © 2009 - The McGraw-Hill Companies srl 8.7 Sample Development 473 so it really opens the door. The class definition is as follows: Public Methods of Door public Door( ) Default constructor that creates a new Door object. public void open() Opens the door. For this simulator class, it displays a simple message dialog. overall design Now let’s study the overall design of the program. In addition to the given helper classes and the Resident class, what other classes should we define for this program? As the number of classes gets larger, we need to plan the classes carefully. For this program, we will define a controller class named Ch8EntranceMonitor whose instance will manage all other objects. We will set this class as the program’s main class. The user interface of the program is handled by the InputHandler class. Its instance is used to allow the user to enter his or her name, room number, and password. After the required input data are entered by the user, a Ch8EntranceMonitor checks the validity of the input data with help from a service Dorm object. If the Dorm object confirms the input data, the controller then instructs another service object, an instance of Door, to open the door. The following is our working design document, and Figure 8.9 is the program diagram. User module InputHandler Ch8EntranceMonitor Dorm JOptionPane Door Resident Figure 8.9 The program diagram for the Ch8EntranceMonitor program. There are three classes in the user module. Java - Fondamenti di programmazione C. Thomas Wu Copyright © 2009 - The McGraw-Hill Companies srl 474 Chapter 8 Exceptions and Assertions 8.7 Sample Development—continued program classes Design Document: Ch8EntranceMonitor Class Purpose Ch8EntranceMonitor The top-level control object manages other objects in the program.This is an instantiable main class. The given predefined class simulates the opening of a door. The given predefined class maintains a list of Resident objects. The user interface class is for handling input routines. Door Dorm InputHandler We will implement the user module in three major steps: development steps 1. Define the Resident class and explore the Dorm class. Start with a program skeleton to test the Resident class. 2. Define the user interface InputHandler class. Modify the top-level control class as necessary. 3. Finalize the code by making improvements and tying up loose ends. Step 1 Development: Program Skeleton step 1 design step 1 code Our first task is to find out about the given Dorm class. (The Door class is a very simple simulator class so there’s not much to explore.) To be able to test-run the Dorm class, we must provide the Resident class, so this will be our first step. The purpose of the skeleton main class in this step is to verify the operations of the Dorm class. The specification for the Resident class was given to us, so our task is to implement it according to the specification. No design work is necessary. When we can interact with an instance of the Dorm class correctly, it confirms that our implementation of the Resident class is working. To verify the key operations of the Dorm class, the top-level supervisor object Ch8EntranceMonitor will open a file and list the contents of the file. Here’s the Resident class: /* Chapter 8 Sample Development: Keyless Entry System. File: Resident.java */ Java - Fondamenti di programmazione C. Thomas Wu Copyright © 2009 - The McGraw-Hill Companies srl 8.7 Sample Development import java.io.*; class Resident implements Serializable { private String name; private String room; private String password; public Resident( ) { this("unassigned", "000", "@13&"); } Data members Constructors public Resident(String name, String room, String pwd) throws IllegalArgumentException { setName(name); setRoom(room); setPassword(pwd); } public String getName( ) { return name; } Accessors public String getPassword( ) { return password; } public String getRoom( ) { return room; } public void setName(String name) { this.name = name; } public void setPassword(String pwd) { int length = pwd.length(); if (length < 4 || length > 8) { throw new IllegalArgumentException(); } else { this.password = pwd; } } public void setRoom(String room) { this.room = room; } } Mutators 475 Java - Fondamenti di programmazione C. Thomas Wu Copyright © 2009 - The McGraw-Hill Companies srl 476 Chapter 8 Exceptions and Assertions 8.7 Sample Development—continued The skeleton main class is defined as follows: /* Chapter 8 Sample Development: Keyless Entry System. (Step 1) File: Ch8EntranceMonitor.java */ import javax.swing.*; import java.io.*; class Ch8EntranceMonitor { //Step 1 main class private Dorm manager; private Scanner scanner; public Ch8EntranceMonitor( ) { manager = new Dorm(); scanner = new Scanner(System.in); } public static void main(String[] args) { Ch8EntranceMonitor sentry = new Ch8EntranceMonitor(); sentry.start(); } public void start( ) { start openFile( ); String roster = manager.getResidentList(); System.out.println(roster); } private void openFile( ) { String filename; openFile while (true) { System.out.println("File to open ('x' to cancel):"); filename = scanner.next(); if (filename.equals("x")) {//input routine is canceled System.out.println("Program is canceled."); System.exit(0); } wu23399_ch08.qxd 12/15/06 19:58 Page 477 Java - Fondamenti di programmazione C. Thomas Wu Copyright © 2009 - The McGraw-Hill Companies srl 8.7 Sample Development 477 try { manager.openFile(filename); return; } catch (FileNotFoundException e) { System.out.println("No such file"); } catch (IOException e) { System.out.println("Error in reading file"); } } } step 1 test The purpose of step 1 testing is to verify that the Dorm class is used correctly to open a file and get the contents of the file. To test it, we need a file that contains the resident information. A sample test file can be created by executing the following program, which we can modify to create other test data files. /* Chapter 8 Sample Development: Keyless Entry System. A simple class to create dummy test data. File: SampleCreateResidentFile.java */ import java.util.*; import java.io.*; class SampleCreateResidentFile { public static void main(String[] args)throws IOException { Resident res; Dorm manager = new Dorm( ); res = new Resident("john", "1-101", "3457"); manager.add(res); res = new Resident("java", "1-102", "4588"); manager.add(res); res = new Resident("jill", "3-232", "8898"); manager.add(res); Java - Fondamenti di programmazione C. Thomas Wu Copyright © 2009 - The McGraw-Hill Companies srl 478 Chapter 8 Exceptions and Assertions 8.7 Sample Development—continued res = new Resident("jack", "3-232", "8008"); manager.add(res); Scanner scanner = new Scanner(System.in); System.out.println("Save to which file:"); String filename = scanner.next(); manager.saveFile(filename); System.exit(0); //terminate the program } } Step 2 Development: Create the User Interface step 2 design In the second development step, we will implement the user interface class InputHandler, whose task is to get three pieces of information. The main controller Ch8EntranceMonitor will call an InputHandler to get input data. An InputHandler will then go through a sequence of getting the three pieces of data. Once the data are entered, Ch8EntranceMonitor will ask the InputHandler for these data. The logic of Ch8EntranceMonitor can be expressed as follows: InputHandler input = new InputHandler(); . . . input.getInput(); String name = input.getName(); String room = input.getRoomNumber(); String pwd = input.getPassword(); Given the input data, we can check for the match as Dorm manager = new Dorm(); . . . Resident res = manager.getResident(name); if (res == null) { System.out.println("Invalid Entry"); wu23399_ch08.qxd 12/15/06 19:58 Page 479 Java - Fondamenti di programmazione C. Thomas Wu Copyright © 2009 - The McGraw-Hill Companies srl 8.7 Sample Development 479 } else if (res.getName().equals(name) && res.getRoom().equals(room) && res.getPassword().equals(password)) { door.open(); } else { System.out.println ("Invalid Entry"); } step 2 code The getInput method of the InputHandler class calls the scanner three times to get the name, room, and password. Each input is recorded in the corresponding data member. The accessors, such as getName, will simply return the value of the requested data member. We will list first the InputHandler class and then the modified Ch8EntranceMonitor class. Here’s the InputHandler class: /* Chapter 8 Sample Development: Keyless Entry System File: InputHandler.java */ import java.util.*; class InputHandler { private static final String BLANK = ""; private private private private Data members String name; String room; String pwd; Scanner scanner; public InputHandler( ) { Constructor name = BLANK; room = BLANK; pwd = BLANK; scanner = new Scanner(System.in); } public void getInput( ) { System.out.print("Enter Name:"); name = scanner.next(); System.out.print("Enter Room No.:"); room = scanner.next(); System.out.print("Enter Password:"); pwd = scanner.next(); } getInput Java - Fondamenti di programmazione C. Thomas Wu Copyright © 2009 - The McGraw-Hill Companies srl 480 Chapter 8 Exceptions and Assertions 8.7 Sample Development—continued public String getName( ) { Accessors return name; } public String getRoom( ) { return room; } public String getPassword( ) { return pwd; } } The main class is now modified to control an InputHandler object and to check entered information as the resident list maintained by a Dorm object. Here’s the step 2 Ch8EntranceMonitor class: /* Chapter 8 Sample Development: Keyless Entry System. File: Ch8EntranceMonitor.java (Step 2) */ import java.util.*; import java.io.*; class Ch8EntranceMonitor { private Dorm manager; private Door door; Data members private InputHandler input; private Scanner scanner; public Ch8EntranceMonitor( ) { manager scanner input door } = = = = new new new new Dorm(); Scanner(System.in); InputHandler(); Door(); Constructors Java - Fondamenti di programmazione C. Thomas Wu Copyright © 2009 - The McGraw-Hill Companies srl 8.7 Sample Development public static void main(String[] args) { Ch8EntranceMonitor sentry = new Ch8EntranceMonitor(); sentry.start(); } public void start( ) { start openFile( ); String roster = manager.getResidentList(); //TEMP System.out.println(roster); //TEMP processInputData(); } private void openFile( ) { String filename; openFile while (true) { System.out.println("File to open ('x' to cancel):"); filename = scanner.next(); if (filename.equals("x")) {//input routine is canceled System.out.println("Program is canceled."); System.exit(0); } try { manager.openFile(filename); return; } catch (FileNotFoundException e) { System.out.println("No such file"); } catch (IOException e) { System.out.println("Error in reading file"); } } } private void processInputData( ) { String name, room, pwd; while (true) { input.getInput(); name = input.getName(); room = input.getRoom(); pwd = input.getPassword(); processInputData 481 Java - Fondamenti di programmazione C. Thomas Wu Copyright © 2009 - The McGraw-Hill Companies srl 482 Chapter 8 Exceptions and Assertions 8.7 Sample Development—continued validate(name, room, pwd); } } private void validate(String name, String room, String password) { Resident res = manager.getResident(name); validate if (res == null) { System.out.println("Invalid Entry"); } else if (res.getName().equals(name) && res.getRoom().equals(room) && res.getPassword().equals(password)) { door.open(); } else { System.out.println("Invalid Entry"); } } } step 2 test Notice that the loop inside the processInputData method is an infinite loop. In other words, when the program starts, it will execute indefinitely. To terminate such a program, you must either close the Command window or select an appropriate menu choice (or click on a toolbar icon) in your Java IDE. We will discuss another way to terminate the program in step 3. The purpose of step 2 testing is to verify the correct behavior of an InputHandler object. We need to test both successful and unsuccessful cases. We must verify that the door is in fact opened when valid information is entered. We must also verify that the error message is displayed when there’s an error in input. We should test invalid cases such as entering nonexistent name, corrent name but wrong password, not entering all information, and so forth. Step 3 Development: Improve and Finalize There are several key improvements we can make to the program. The first and foremost is the improved user interface. Instead of getting three pieces of data individually by using a scanner, it would be nicer to have a frame window such as the one shown in Figure 8.10, where the user can enter all three pieces of information.We will describe how to develop such a frame window in Chapter 14. Java - Fondamenti di programmazione C. Thomas Wu Copyright © 2009 - The McGraw-Hill Companies srl Summary 483 Another improvement is to allow the administrator to terminate the program by entering special code.This is left as an exercise. Figure 8.10 A frame window that allows the user to enter the three pieces of information together. Notice the input entered for the password is displayed back to the user as a sequence of asterisks. S u m m a r y • • • • • • • Two techniques to improve program reliability are exception handling and assertion. Exception handling is another type of control flow. An exception represents an error condition, and when it occurs, we say an exception is thrown. A thrown exception must be handled by either catching it or propagating it to other methods. If the program does include code to handle the thrown exceptions, then the system will handle them. A single method can be both a catcher and a propagator of an exception. The standard classes described or used in this chapter are Throwable RuntimeException Error IllegalArgumentException Exception InputMismatchException IOException • • The assertion feature is new to Java 2 SDK 1.4. You must use this version of the compiler to use assertions in the program. The assertion feature is used to detect internal logic errors.