Refactoring


Creating a string to present to the user often involves concatenating several substrings and printable representations of primitives and objects. The Student constructor provides an example:

 public Student(String fullName) {    this.name = fullName;    credits = 0;    List<String> nameParts = split(fullName);    final int maximumNumberOfNameParts = 3;    if (nameParts.size() > maximumNumberOfNameParts) {       String message =          "Student name '" + fullName +          "' contains more than " + maximumNumberOfNameParts +          " parts";       throw new StudentNameFormatException(message);    }    setName(nameParts); } 

In the Student constructor, you combine five separate elements to form a single stringthree substrings, the full name, and a constant representing the maximum number of name parts allowed. You must likewise construct a string in the test method. Concatenations of larger strings, with more embedded information, become very difficult to read and maintain.

The String class supplies a class method named format. The format method allows you to pass a format String and a variable number of arguments (see the information on varargs in Lesson 7). In return, you receive a string formatted to your needs. A format String contains elements known as format specifiers. These specifiers are placeholders for the arguments. A format specifier tells the formatter how to interpret and format each argument. Often, but not always, you will have one format specifier for each argument.

For those of you who are familiar with the language C, Java's String formatting capability derives from a comparable C feature used in functions such as printf. The Java implementation is similar but provides more features and safety. Do not assume that a Java format specifier works the same as an equivalent C format specifier.

Start with testBadlyFormattedName and replace the concatenation with a call to format. Also, introduce a class constant for the maximum number of name parts allowed.

 public void testBadlyFormattedName() {    final String studentName = "a b c d";    try {       new Student(studentName);       fail("expected exception from 4-part name");    }    catch (StudentNameFormatException expectedException) {       assertEquals(          String.format("Student name '%s' contains more than %d parts",              studentName, Student.MAX_NAME_PARTS),          expectedException.getMessage());    } } 

The call to format has three arguments: the format string and two format string arguments. Within the format string are two format specifiers: %s and %d. The first format specifier corresponds to the first argument, studentName. The second format specifier corresponds to the second argument, Student.MAX_NAME_PARTS. A format specifier always begins with a percent sign (%) and ends with a conversion. A conversion is a character or character that tells the format method how to interpret the corresponding argument.

The character s, as in %s, indicates a string conversion. When the format method encounters a string conversion in a format specifier, it replaces the format specifier with the corresponding argument. In the test, format replaces %s with the contents of studentName:

 Student name 'a b c d' contains more than %d parts 

The character d, as in %d, indicates an integral conversion to decimal. In the test, format replaces %d with the value of Student.MAX_NAME_PARTS (which you will set to the int literal 3):

 Student name 'a b c d' contains more than 3 parts 

The String method format is a utility method that delegates all the work to an instance of the java.util.Formatter class. The Formatter class supplies more than a dozen different conversions, including date conversions and more-sophisticated conversions for numeric values.

One very useful conversion is n, which stands for "new line." If you provide the format specifier %n, the formatter replaces it with the platform-specific line separator (usually "\n" or "\r\n"). This saves you from having to provide code that accesses the system line separator property.

Some of the other things Formatter supports:

  • internationalization

  • the ability to provide arguments in an order that does not match the order of format specifiers

  • sending output incrementally to a StringBuilder or other sink

The API documentation for java.util.Formatter is long and detailed. Refer to it to understand how to use additional conversions or how to work with the additional features of Formatter.

As a final step, you can refactor to introduce a class constant for the error message. The following code also shows the implementation in Student:

 // StudentTest.java public void testBadlyFormattedName() {    final String studentName = "a b c d";    try {       new Student(studentName);       fail("expected exception from 4-part name");    }    catch (StudentNameFormatException expectedException)  {       assertEquals(          String.format(Student.TOO_MANY_NAME_PARTS_MSG,                        studentName, Student.MAX_NAME_PARTS),          expectedException.getMessage());    } } // Student.java static final String TOO_MANY_NAME_PARTS_MSG =    "Student name '%s' contains more than %d parts"; ... public Student(String fullName) {    this.name = fullName;    credits = 0;    List<String> nameParts = split(fullName);    if (nameParts.size() > MAX_NAME_PARTS) {      String message =         String.format(Student.TOO_MANY_NAME_PARTS_MSG,                       fullName, MAX_NAME_PARTS);       throw new StudentNameFormatException(message);    }    setName(nameParts); } 



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