Refactoring


Splitting a String

While either the hand-coded while loop or for loop in the Student method split will work, the resulting code is fairly complex. Java provides at least two more ways to break apart String objects into tokens. First, you will learn how to use the StringTokenizer class to break an input string into individual components, or tokens. The StringTokenizer class is considered a legacy class, meaning that Sun wants you to supplant its use with something better. That something better is the split method, defined on String. Once you have coded the solution using StringTokenizer, you will recode it to use String.split.

Up until Java 1.4, Sun recommended the StringTokenizer as the solution for tokenizing strings. You will still encounter a lot of code that uses StringTokenizer; that's why you'll learn it here. In Java 1.4, Sun introduced the regular expressions API. Regular expressions are specifications for pattern-matching against text. A robust, standardized language defines the patterns you can express with regular expressions. The newer String.split method uses a regular expression, making it far more effective than the StringTokenizer solution. For more information about the regular expressions API, see Additional Lesson III.

The constructor for the class java.util.StringTokenizer takes as parameters an input String and a String representing a list of character delimiters. StringTokenizer then breaks the input String into tokens, allowing you to iterate through each token in turn. The while loop provides the best mechanism for traversing these tokens:

 private List<String> split(String name) {    List<String> results = new ArrayList<String>();    StringTokenizer tokenizer = new StringTokenizer(name, " ");    while (tokenizer.hasMoreTokens())       results.add(tokenizer.nextToken());    return results; } 

Sending the message hasMoreTokens to a StringTokenizer returns false once there are no more tokens. The message nextToken returns the next token available. The StringTokenizer code uses the list of delimiters to determine what separates tokens. The example here uses only the space character to delineate tokens.

Splitting a String: String.split

The String class contains a split method that makes the job of tokenizing a full name even simpler than using StringTokenizer. Using String.split would allow you to eliminate your entire Student.split method with one line of code.

Here is a stab at the refactoring:

 public Student(String fullName) {    this.name = fullName;    credits = 0;    List<String> nameParts = split(fullName);    setName(nameParts); } private List<String> split(String name) {    return Arrays.asList(name.split(" "));  // this doesn't work! } private void setName(List<String> nameParts) {    this.lastName = removeLast(nameParts);    String name = removeLast(nameParts);    if (nameParts.isEmpty())       this.firstName = name;    else {       this.middleName = name;       this.firstName = removeLast(nameParts);    } } private String removeLast(List<String> list) {    if (list.isEmpty())       return "";    return list.remove(list.size() - 1); } 

Part of your goal should be to make changes with as little impact to other code as possible. The single line of code that now appears in your split method seems like it should do the trick:

 return Arrays.asList(name.split(" "));  // this doesn't work! 

The split method takes a single parameter, the list of characters that represent word boundaries. It returns an array of words. The Arrays class method asList takes an array and returns a List representation of that array.

One would think that this line of code would be a suitable replacement for the previous code that constructed the list using StringTokenizer. But it -doesn't work. You will receive many JUnit errors indicating that the remove call in your removeLast method now generates something known as an UnsupportedOperationException.

The problem is in the Arrays method asList. According to the Java API documentation for asList, it returns a list backed by the array you pass as parameter. The list is a view onto the array, which does not go away. The list reflects any changes you make to the array.

Since the list is backed by an array, and since you cannot remove elements from an array, Java prohibits the remove call.

A solution that will work is to iterate through the results of split using a for-each loop. As with the StringTokenizer solution, you can add each word to a results array.

 private List<String> split(String fullName) {    List<String> results = new ArrayList<String>();    for (String name: fullName.split(" "))       results.add(name);    return results; } 

The split method uses a Java feature known as regular expressions. A regular expression is a set of syntactical elements and symbols that is used to match text. You have likely used a simple form of regular expression when listing files in a directory. You use the asterisk (*) as a wildcard symbol to match any sequence of characters in a filename. See Additional Lesson III for an overview of the powerful regular expressions feature.



Agile Java. Crafting Code with Test-Driven Development
Agile Javaв„ў: Crafting Code with Test-Driven Development
ISBN: 0131482394
EAN: 2147483647
Year: 2003
Pages: 391
Authors: Jeff Langr

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net