Refactoring Session


Here's an overview of the steps you'll undertake in this refactoring:

1.

Construct session objects using a Course object. Impacts: SessionTest, Session, CourseSessionTest, SummerCourseSessionTest, RosterReporterTest, CourseReportTest.

2.

Change the creation methods in CourseSession and SummerCourse-Session to take a Course as a parameter.

3.

Modify CourseSession and SummerCourseSession constructors to take a Course.

4.

Modify the Session constructor to take a Course.

5.

Store a Course reference in Session instead of department and number strings.

You will make these changes incrementally. With each change, you'll use the compiler to help you find problems, then run your tests to ensure that you didn't break anything.

Starting in SessionTest:

Currently, the abstract method createSession constructs Session objects using department and number strings:

 abstract protected Session createSession(    String department, String number, Date startDate); 

You want to change this creation method to reflect the need to construct Session objects using a Course object:

 abstract public class SessionTest extends TestCase {    ...    abstract protected Session createSession(       Course course, Date startDate); 

This change will necessitate quite a few other changes, but overall it shouldn't take more than 5 minutes to make them. In SessionTest, you will need to change both the setUp method and testComparable. Here's the existing setUp method in SessionTest (I've boldfaced the code that will need to change):

 public void setUp() {    startDate = createDate(2003, 1, 6);    session = createSession("ENGL", "101", startDate);    session.setNumberOfCredits(CREDITS); } 

I've shown the change to setUp here; changes to testComparable are similar:

 protected void setUp() {    startDate = new Date();    session = createSession(new Course("ENGL", "101"), startDate);    session.setNumberOfCredits(CREDITS); } 

Use the compiler to guide you through making these changes. As you fix one problem, the compiler can take you to the next problem to fix.

You will need to change both SummerCourseSessionTest and CourseSessionTest. In the interest of incrementalism, try to make the smallest amount of changes that will get you to a green bar. You can modify both CourseSessionTest and SummerCourseSessionTest without having to touch CourseSession or SummerCourseSession.

Here are the changes to the tests. This time I've included the old lines of code from the previous version of the code and struck them out so you could see the difference line to line. I'll use this approach for the remainder of the refactoring.

 // CourseSessionTest ... public class CourseSessionTest extends SessionTest {    public void testCourseDates() {       Date startDate = DateUtil.createDate(2003, 1, 6);       Session session = createSession("ENGL", "200", startDate);       Session session = createSession(createCourse(), startDate);       Date sixteenWeeksOut = createDate(2003, 4, 25);       assertEquals(sixteenWeeksOut, session.getEndDate());    }    public void testCount() {       CourseSession.resetCount();       createSession("", "", new Date());       createSession(createCourse(), new Date());       assertEquals(1, CourseSession.getCount());       createSession("", "", new Date());       createSession(createCourse(), new Date());       assertEquals(2, CourseSession.getCount());    }    private Course createCourse() {       return new Course("ENGL", "101");    }    protected Session createSession(          String department, String number, Date date) {       return CourseSession.create(department, number, date);    }    protected Session createSession(Course course, Date date) {       return CourseSession.create(          course.getDepartment(), course.getNumber(), date);    } } // SummerCourseSessionTest.java package sis.summer; import junit.framework.*; import java.util.*; import sis.studentinfo.*; public class SummerCourseSessionTest extends SessionTest {    public void testEndDate() {       Date startDate = DateUtil.createDate(2003, 6, 9);       Session session = createSession("ENGL", "200", startDate);       Session session =          createSession(new Course("ENGL", "200"), startDate);       Date eightWeeksOut = DateUtil.createDate(2003, 8, 1);       assertEquals(eightWeeksOut, session.getEndDate());    }    protected Session createSession(          String department, String number, Date date) {       return SummerCourseSession.create(department, number, date);    }    protected Session createSession(Course course, Date date) {       return SummerCourseSession.create(          course.getDepartment(), course.getNumber(), date);    } } 

Tests should pass at this point. Now modify the Session subclass creation methods to take a Course directly.

 // CourseSessionTest.java protected Session createSession(Course course, Date date) {    return CourseSession.create(          course.getDepartment(), course.getNumber(), date);    return CourseSession.create(course, date); } // SummerCourseSessionTest.java protected Session createSession(Course course, Date date) {    return SummerCourseSession.create(       course.getDepartment(), course.getNumber(), date);    return SummerCourseSession.create(course, date); } 

This change impacts RosterReporterTest:

 package sis.report; import junit.framework.TestCase; import sis.studentinfo.*; import static sis.report.ReportConstant.NEWLINE; public class RosterReporterTest extends TestCase {    public void testRosterReport() {       Session session =          CourseSession.create(             "ENGL", "101", DateUtil.createDate(2003, 1, 6));       Session session =          CourseSession.create(             new Course("ENGL", "101"),             DateUtil.createDate(2003, 1, 6));       ...    } } 

And also CourseReportTest[3]:

[3] I've taken some liberties by updating both RosterReporterTest and CourseReportTest with Java features you have learned since originally coding them. For example, the code now assigns a CourseSession object to a Session reference. Doing so will also force you to change to RosterReporter and CourseReport.

 package sis.report; import junit.framework.*; import java.util.*; import sis.studentinfo.*; import static sis.report.ReportConstant.NEWLINE; public class CourseReportTest extends TestCase {    public void testReport() {       final Date date = new Date();       CourseReport report = new CourseReport();       report.add(CourseSession.create("ENGL", "101", date));       report.add(CourseSession.create("CZEC", "200", date));       report.add(CourseSession.create("ITAL", "410", date));       report.add(CourseSession.create("CZEC", "220", date));       report.add(CourseSession.create("ITAL", "330", date));       report.add(create("ENGL", "101", date));       report.add(create("CZEC", "200", date));       report.add(create("ITAL", "410", date));       report.add(create("CZEC", "220", date));       report.add(create("ITAL", "330", date));       assertEquals(          String.format(             "CZEC 200%n" +             "CZEC 220%n" +             "ENGL 101%n" +             "ITAL 330%n" +             "ITAL 410%n"),          report.text());    } private Session create(String name, String number, Date date) {    return CourseSession.create(new Course(name, number), date);    } } 

Change the creation methods in CourseSession and SummerCourseSession to take a Course as a parameter, butfor the momentcontinue to pass along the department and number to the constructor.

 // CourseSession.java public static Session create(       String department, String number, Date startDate) {    incrementCount();    return new CourseSession(department, number, startDate) ; } public static Session create(Course course, Date startDate) {    incrementCount();    return new CourseSession(       course.getDepartment(), course.getNumber(), startDate); } // SummerCourseSession.java public static SummerCourseSession create(       String department, String number, Date startDate) {    return new SummerCourseSession(department, number, startDate); } public static Session create(Course course, Date startDate) {    return new SummerCourseSession(       course.getDepartment(), course.getNumber(), startDate); } 

Tests pass. Now push the course into the constructor and rerun the tests.

 // CourseSession.java public static Session create(Course course, Date startDate) {    incrementCount();    return new CourseSession(course, startDate); } protected CourseSession(       String department, String number, Date startDate) {    super(department, number, startDate); } protected CourseSession(Course course, Date startDate) {    super(course.getDepartment(), course.getNumber(), startDate); } // SummerCourseSession.java public static Session create(Course course, Date startDate) {    return new SummerCourseSession(course, startDate); } private SummerCourseSession(       String department, String number, Date startDate) {    super(department, number, startDate); } private SummerCourseSession(Course course, Date startDate) {    super(course.getDepartment(), course.getNumber(), startDate); } 

Tedious? Perhaps. Simple? Each one of the steps in this refactoring is very basic and takes only seconds to execute. Safe? Extremely. The overall time expenditure should only be a few minutes before everything is working. You get to see a green bar several times during the gradual code transformation. The foremost authority on refactoring, Martin Fowler, suggests that you can never run your tests too frequently.[4]

[4] [Fowler2000], p. 94.

The alternative would be to make the changes all at once and hope they work. You might save a couple of minutes, but there is a likely possibility that you will make a mistake. Correcting the mistake will take you more time than you saved.

You're almost there. Push the Course object into the superclass Session constructor.

 // CourseSession.java protected CourseSession(Course course, Date startDate) {    super(course, startDate); } // SummerCourseSession.java private SummerCourseSession(Course course, Date startDate) {    super(course, startDate); } // Session.java protected Session(       String department, String number, Date startDate) {    this.department = department;    this.number = number;    this.startDate = startDate; } protected Session(Course course, Date startDate) {    this.department = course.getDepartment();    this.number = course.getNumber();    this.startDate = startDate; } 

Finally, you can change the Session class to store a Course reference instead of the separate department and number String references.

 // Session.java abstract public class Session implements Iterable<Student> {    private String department;    private String number;    private Course course;    // ...    protected Session(Course course, Date startDate) {       this.course = course;       this.startDate = startDate;    }    String getDepartment() {       return department;       return course.getDepartment();    }    String getNumber() {       return number;       return course.getNumber();    }    // ... 



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