|
|
Example introduces: (1) the use of the pow and sqrt class methods contained in the Math class, and (2) a further (to that given in Section 1) illustration of how to return values from methods using the keyword return. Note also that the Triangle class will be reused in the following lecture.
1. OVERVIEW |
When developing a new class it makes software engineering sense to test the class as thoroughly as possible before using it on a real application. We have already looked at two testing techniques arithmetic testing and data validation. Another technique is to build a test harness to automate the arithmetic testing process. This is a specially written Java class (the same as any other) designed to "exercise" a newly developed class.
We shall be illustrating such a test harness in Section 5, which will be used to test the Java code produced in the example problem presented in Section 4. Before we look at the example problem and the test harness we will (Sections 2 and 3) briefly reconsider method calls, the process of returning a value after a method call and argument passing.
2. METHOD CALLS AND THE return KEYWORD |
The process of calling one method from within another method (as illustrated previously) is referred to as a method call (in imperative language the process is known as routine invocation). The method call names the method and supplies the arguments (if any) to be supplied to the method. As the result of a method call "control" is passed from the calling method to the target method. On completion control is returned to the point immediately after the call (Figure 1). ![]() Figure 1: Flow of control with respect to method calls |
If the method we are calling is defined in some other class then we must call it by either:
Consider the code fragment given in Table 1. Here we define a class with a private instance variable and a public instance method which returns the instance variable multiplied by 3. The code given in Table 2 presents an application class that makes use of the class given in Table 1. Two instances of the class ExampleClass, instance1 and instance2 are created; each of which calls the treble method in turn. For the first instance the return value is assigned to a local variable (result1), for the second instance the call to the treble method has been embedded in the output statement. The resulting output will be as follows:
|
// Example Class Version 1 // Frans Coenen // 25 July 2000 // Dept Computer Science, University of Liverpool class ExampleClassVer1 { // ------------------- FIELDS ------------------------ private int value; // -----------------CONSTRUCTORS --------------------- /* Constructor */ public ExampleClassVer1(int num) { value = num; } // ------------------ METHODS ------------------------ public int treble() { return(value*3); } } |
Table 1: Example class (version 1)
// Application Class // Frans Coenen // 25 July 2000 // Dept Computer Science, University of Liverpool class ApplicationClass { // ------------------- FIELDS ------------------------ /* None */ // ------------------ METHODS ------------------------ /* Main method */ public static void main(String[] args) { // Create two instances of the Example class ExampleClassVer1 instance1 = new ExampleClassVer1(2); ExampleClassVer1 instance2 = new ExampleClassVer1(3); // Process int result1 = instance1.treble(); System.out.println("Instance 1 = " + result1); System.out.println("Instance 2 = " + instance2.treble()); } } |
Table 2: Application class with method calls
3. METHOD CALLS AND ARGUMENT PASSING |
In the examples given so far we have included arguments in the main methods (the yet to be explained String[] args) and constructors, both can be considered to be special kinds of method. Although we have noted that arguments can be passed to "ordinary" methods as well, we have not yet looked at example of this --- we will do this in this section. (note that the main method is a bit different in that it must have an argument, in all other cases arguments are optional). We pass arguments to ordinary instance and class methods in the same way that we have passed arguments to constructors. Methods, including constructors (but not main), can have any number of arguments (main has one). We have seen that when defining a method we include the arguments (if any) in the signature of the method. The argumenets are referred to as the formal parameters of the method. An example is given in Table 3 where a revised version of the code presented in Table 1 is given. Note that in this case the value "field" is passed as an argument to the treble method, in which case value is no longer a field but a simple data item that happens to be a formal parameter. |
Thus, in the example, we have no fields and therefore we do not need a specific constructor to set the value of the field --- instead we use the default constructor which is created automatically on compilation. In Table 4 we present an application class to be used in conjunction with the code presented in Table 3. Note that in this case we create only one instance of the class ExampleClassVer2 which we can use to call the instance method treble any number of times. When we pass a value to the treble method, for example 2, we say that the value 2 becomes the actual parameter to the method. So on the first call the value 2 is the actual parameter, and on the second call the value 3 is the actual parameter. If we have more than one argument the actual parameters are matched up to the formal parameters in the order that they are listed in the signature of the method. That advantage offered by the argument passing mechanism is that the same method can be used to perfoma some operation any number of times but with different input data. |
// Example Class // Frans Coenen // 14 July 2003 // Dept Computer Science, University of Liverpool class ExampleClassVer2 { // ------------------- FIELDS ------------------------ /* None */ // -----------------CONSTRUCTORS --------------------- /* Default constructor only */ // ------------------ METHODS ------------------------ public int treble(int value) { return(value*3); } } |
Table 3: Example class (version 2)
// Application Class // Frans Coenen // 25 July 2000 // Dept Computer Science, University of Liverpool class ApplicationClass { // ------------------- FIELDS ------------------------ /* None */ // ------------------ METHODS ------------------------ /* Main method */ public static void main(String[] args) { // Create an instance of the Example class ExampleClassVer2 instance = new ExampleClassVer2(); // Process int result1 = instance.treble(2); System.out.println("Instance 1 = " + result1); System.out.println("Instance 2 = " + instance.treble(3)); } } |
Table 4: Application class with method calls and argument passing
Note: the examples given in Tables 1 to 4 are intended purely to illustrate the concepts of passing arguments to and from methods --- as such the examples are somewhat contrived.
4. EXAMPLE PROBLEM - PYTHAGORAS4.2 AnalysisA class diagram for the proposed solution is presented in Figure 3. 4.3 DesignFrom the above (Figure 3) the design comprises two classes, Triangle and PythagorasApp. 4.3.1 Triangle Class
4.3.2 PythagorasApp Class
4.4. Implementation4.4.1 Triangle class
Table 5: Triangle Class definition 4.4.1 PythagorasApp class
Table 6: Pythagoras application program
Data validation testing: We should also include some tests using input data which is deliberately of the wrong type or to many inputs, no inputs etc. |
NOTE: We will be reusing the Triangle class again!
5. CREATING A "TEST HARNESS" |
From the above 9 test cases are required to test the Triangle class. We can reduce the testing effort by writing a test harness (or "driver program"). The aim is to provide methods, in the test harness, to exercise each of the methods in our newly developed class. Some suitable Java code to do this is given in Table 7. The code comprises three methods, a main method and two others: includeAdjValue and makeCalculationAndOutput. The main method includes three calls to includeAdjValue, with a different value for the formal parameter on each occasion. The includeAdjValue method then calls makeCalculationAndOutput three times, with the received argument, but alos with a different second argument on each occasion. The makeCalculationAndOutput method is therefore called 3x3 times in total, each time with different parameters. A schematic, illustrating the "flow of control" through the software, is given in Figure 6. |
Note that the methods included in the test harness given in Table 7 are all class (static) methods which can be called directly from within a class. Hopefully the results produced by the test harness will be comparable with the expected test results given in the test case table. Table 8 gives the output produced by this test harness.
Table 8: Output from test harness |
// PYTHAGORAS TEST CLASS // Frans Coenen // Friday 5 March 1999 // University of Liverpool import java.io.*; class PythagorasTest { // ------------------- FIELDS ------------------------ /* None */ // ------------------ METHODS ------------------------ /* Main method */ public static void main(String[] args) { includeAdjValue(4.0); includeAdjValue(0.0); includeAdjValue(-4.0); } /* Add adjacent value */ public static void includeAdjValue(double opposite) { makeCalculationAndOutput(opposite,3.0); makeCalculationAndOutput(opposite,0.0); makeCalculationAndOutput(opposite,-3.0); } /* Make calculation and output */ public static void makeCalculationAndOutput(double oppositeSide, double adjacentSide) { // Call constructor with input Triangle newTriangle = new Triangle(oppositeSide,adjacentSide); // Calculate hypotenuse newTriangle.calculateHypotenuse(); // Output result System.out.println("Given: opposite = " + oppositeSide + ", adjacent = " + adjacentSide); double hypotenuseSide = newTriangle.getHypotenuse(); System.out.println("Result: hypotenuse = " + hypotenuseSide); } } |
Table 7: "Test harness" code
Note that when choosing a name for a method the designer should be influenced by what the method does and not what the calling method(s) might do.
Figure 6 shows an Activity Diagram for the application. It shows how "control" flows through the program. We start at the top at what is knowm as the "riser" and end at the bottom at what is known as the "sink" (the analogy is that of water flowing through pipes). The boxes with rounded corners indicate activities (method calls and calculations in this case), and the red arrows (directed arcs) the flow of control (water!). The dotted boxes in the diagram indicate methods which encompass a number of activities.
Thus we commence at the riser (the main method) and call the adajacentValue method with the argument 4.0. This then becomes the actual parameter for this method and flow of control passes from main to adajacentValue. In the adajacentValue method the makeCalc method is called three times in sequence. On the first occasion the actual parameter given to adajacentValue (4.0) and the value 3.0 are passed as arguments to the makeCalc method. Control is temporarily passed to the makeCalc method which does the necessary calculation, outputs the result (5.0) and then passes control back to the calling method. On the second occasion the adajacentValue method's actual parameter 4.0 and the value 0.0 are passed as arguments to the makeCalc method and the calculation is repeated. On the third occasion the actual parameter 4.0 and the value -3.0 are passed to makeCalc. Once makeCalc has completed its processing flow of control returns to main where the process is repeated but this time the formal parameter for the adajacentValue is passed the actual parameter 0.0. And so on.
Fig 6: Activity diagram showing the "flow of control" through the test harness program given in Table 3
Created and maintained by Frans Coenen. Last updated 10 February 2015