15. Back to Polymorphism

Part of CS:2820 Object Oriented Software Development Notes, Fall 2015
by Douglas W. Jones
THE UNIVERSITY OF IOWA Department of Computer Science

 

Where were we?

We had proposed this code:

                        String command = sc.next();
                        if (("intersection".equals( command ))
                        ||  ("i".equals( command ))) {
                                // Bug:  What if no sc.next?
                                String name = sc.next();
                                // Bug:  What if no sc.next?
                                String kind = sc.next();
                                if (kind == "stoplight") {
                                        inters.add( new StopLight( sc, name ) );
                                } else {
                                        // Bug:  How do we undo sc.next()?
                                        inters.add( new NoStop( sc, name ) );
                                }
                        } else if (("road".equals( command ))

This code is missing some crucial bits, but even if we fixed it, the biggest thing we are missing are the initializers for the new interseciton subclasses.

Subclass Initializers.

Since we never intend to allow anyone to initialize an object of class Intersection, the first thing we do (after saving a copy of the old initializer so serve as a template for the subclass initializers) is to replace it with this:

        // initializer
        private Intersection() {}

Declaring the initializer to be private prevents any code from outside class Intersection from creating any objects of this class.

Now, we add initializers to the two subclasses of Intersection. For example, the initializer for NoStop initially looks something like this:

        public NoStop( Scanner sc, String name ) {
                // scan and process one intersection
                if (RoadNetwork.findIntersection( name ) != null) {
                        Errors.warning( "Intersection "
                                      + name
                                      + " -- redefined."
                        );
                        // Bug:  Should we prevent creation of this object?
                }
                String skip = sc.nextLine();
                // Bug:  What if more junk at end of line (skip not empty)
        }

The code for StopLight is similar. Before we give this, though, note that we have a bug notice that is repeated three times in our code. In class Road and again, in classes NoStop and StopLight, we have repeated the same basic bug notice asking how we detect improper end of line. What we need is a service method to solve this problem in one place, instead of duplicating code everywhere.

Input Scanning Support

Just as error reporting was worthy of a separate class, so is support for common problems encountered in scanning the input. We'll call this class ScanSupport, and we'll begin by developing just one method, lineEnd() that is used to cleanly scan the end of each line and complain about irregularities that might be encountered there.

Later, we can add some common methods for other problems that occur repeatedly in the code. For example, each time we scan an intersection name, we ought to check that it is a legal name and not, for example, a floating point number or a quoted character string.

Our initial version of the LineEnd() method just preserves the bug notices we found in the three original contexts where we needed to scan over the end of line:

/** Input scanning support methods
 */
class ScanSupport {
        /** Force there to be a line end here, complain if not
         */
        static void lineEnd( Scanner sc ) {
                String skip = sc.nextLine();
                // Bug:  What if more junk at end of line (skip not empty)
        }
}

Initially, we needed just one parameter, the scanner we are using to read the input. Once we start doing something about the bug, however, we will need a second parameter, the error message to print if errors are detected in the the line end. While we are at it, we can add the code to detect the most obvious of the errors, a non-empty line end:

        static void lineEnd( Scanner sc, String message ) {
                String skip = sc.nextLine();
                if (!"".equals( skip )) {
                        // Bug:  do we want to allow comments here
                        Errors.warning( message + " -- expected a newline" );
                }
                // Bug:  what if sc.nextLine() was illegal (illegal state)
        }

We've corrected one bug notice, but we've added to new ones: What if it was illegal to call sc.nextLine() because there was no next line in the input file? This can occur at the end of an ill-formed input file where there is no newline character at the end of the final line of the file. Some text editors, such as vi do not permit you to create such files. Others do, and we ought to handle this gracefully.

The other bug notice is a suggested enhancement. We have the option here of supporting a uniform comment notation, for example, we might consider any text at the end of line that starts with two consecutive dashes to be a comment, so this kind of input would be legal:

intersection A -- this is really a traffic circle
road A B 12.5  -- this is half of a 2-lane road

Adding support for this is a nice exercise.

Initializing the Subclasses

It is time to return to the initializers for our subclasses. Here is the initializer for class StopLight augmented to use the above code:

        // initializer
        public Intersection( Scanner sc, String name ) {
                // scan and process one intersection
                if (RoadNetwork.findIntersection( name ) != null) {
                        Errors.warning( "Intersection "
                                      + name
                                      + " redefined."
                        );
                        // Bug:  Should we prevent creation of this object?
                }
                ScanSupport.lineEnd(
                        sc,
                        "Intersection " + name + " stoplight"
			// Bug:  We do these concatenations with every call
                )
        }

This initializer will be the basis of some extended discussion because of the two bug notices: First, in the event that an attempt is made to redefine an intersection that already exists, we need to suppress the definition, and second, it is foolish to do computationally complex things like string concatenation before any need arises. Dynamically creating an error message before every call is foolish if we only need that message occasionally, in those few lines that contain errors.