Skip to content

Text file processor kata for application to Root Insurance.

Notifications You must be signed in to change notification settings

jchaffin87/root-application-kata

Repository files navigation

My Attempt at the Root Code Challenge

Specs:

  • Written in: Java
  • Tested with: Junit
  • Built with: Gradle

To run the app:

  1. cd into the project folder
  2. use java -jar RootDrivingReportGenerator.jar (yourFile.txt) to run the jar and pass it the file you want it to process

My thought process in writing this little app:

Initial thoughts

When I was reading the requirements in the problem statement I realized that I would need to break out the data from each line and associate each piece of data with either a Driver or a Trip, depending on the command at the beginning of the line. My first thought was to create both Driver and Trip objects which I could associated the pieces of data with as data members. I thought doing so would make those pieces of data easier to work with and do operations on, since I wasn't just going to be returning the data as presented back in a different format, but using it to create different pieces of data which would be used in the generation of the final report as output. I also felt that doing it this way would make it easier to make sure that each piece of data in the input file would stay strongly associated with its corresponding Driver or Trip.

Creating the Drivers and Trips

As such, I started out by creating a Driver class with a String data member called name and a constructor which would take String name as an argument. I also created a Trip class which held data members for the driver's name, the start time, the stop time, and the distance (as presented in the input file), along with a constructor which would take each of these as an argument. I also had Eclipse generate getters for the starting data members of both classes.

After this, I realized I needed to store the trips so that I could iterate through them and assign them to their appropriate drivers so I started out making a TripRepository class which just held an ArrayList of Trips in which each instance of Trip would be placed upon their creation during the parsing of the file and later be assigned to their drivers by a separate method. I decided it would be simpler though to just have each instance of Driver have an ArrayList of Trips that are placed there upon their creation in a single step. This way, when I later wrote the methods which would determine the total distance driven and average speed of each driver, I could have that logic in the Driver class itself and let it refer to that driver's trip data without having to access any outside classes. Along with this being a simpler approach, I felt it was more intuitive and would lead to looser coupling in the application. I later ended up splitting the creation of trips and assignment of trips to drivers into separate methods based on the principle that methods should Do One Thing, but I still felt that having the Driver instances own the ArrayList holding the trips belonging to them made the most sense.

Parsing the Input

I then started on the actual parsing of the input file and creation of the Drivers and Trips which would be used to ultimately generate the desired output as a driving history report. I started by creating a FileParser class which would hold methods that would take in the input file and use the data on each line to create either a Driver or Trip and store it for later use in generating the report. I looked at and played with various ways of iterating through the document and each line in the document to pull each piece of data out and assign it to an object, but I ended up just using Scanner objects with their next() and nextLine() methods which respectively have the default delimiters of spaces and carriage returns, since the format of the input was such that this worked well and it these would be the easiest and simplest things to use for the purpose. With this decision made I went on to write the methods which would create and store Driver and Trip objects using the data parsed from each line of the input file as their fields. I ended up storing the Drivers in an ArrayList owned by the FileParser class, mainly because that's where it made the most sense to me to store them, given that I could ultimately reference them with a simple getDriverData() method call on the FileParser instance I planned to put into my ReportGenerator class and minimize the number of classes involved in creating the report. I also made some effort to keep improperly formatted input from stopping the operation of the app by having my methods which parsed the file ignore any lines without either the "Driver" or "Trip" command at the beginning and ignore any lines which were missing the Driver's name, since they would prevent the creation of any instance of either type of object. It could be more robust, but I didn't know how far I should go with that, given the requirements of the challenge. I also had the method which saved the assigned the Trip objects to their drivers discard any with speeds of less than 5 mph or more than 100 mph, as per the instructions in the problem statement.

Calculating the data in the report

In order to generate the correct output for the report I needed to calculate the total distance each driver had driven, along with the average speed the driver had driven. While getting the total distance driven was a fairly simple matter of iterating through each driver's trips and adding the trips' distances together, calculating the average speed was a two step process of first finding the speed of each trip based on the start time, stop time, and distance traveled and then calculating the average speed of all the trips belonging to each driver based on those speeds. I decided that I should split those into two separate classes since they were two significant operations and not doing so would violate best practices and make testing more complicated. As such, I decided that the getSpeed() method should belong to the Trip class, since each Trip would have its own speed, and the getAverageSpeed() method should belong to the Driver class, since each driver would have their own average speed driven and that was where the Trips whose speeds would be used in this calculation were held.

Putting it all together and generating the report

When I got to the point where I was ready to generate the output and create the report I decided to create a separate ReportGenerator class for the sake of flexibility, extensibility, and separation of concerns and have it own an instance of the FileParser class which it would use to generate and print the appropriate output to the console. I also went back and combined all the methods in the FileParser class involved in parsing the file and creating and storing the File and Driver objects into one class into one parseFile() method, in order to minimize the number of method calls the ReportGenerator class would have to make on its instance of FileParser. At this point it was a relatively simple matter of having the generateReport() method of the ReportGenerator class have its instance of FileParser parse the input file, getting the driver data from the FileParser, iterating through the driver data, and printing the appropriate line to the console for each driver given the calculated total distances driven and average speeds of each. I also included a conditional in order to print different output for drivers without any trips assigned to them and avoid having errors crop up from trying to access data which didn't exist for a specific driver.

After that, all I had to do was create the ReportGeneratorApp class which created a file based on the args passed to its main method, created an instance of ReportGenerator and pass the file to the ReportGenerator instance. At that point, I created the jar file and I am now writing this README before creating the git bundle and sending this project to Root for consideration.

Testing

Throughout the process of creating this app I used the Test-Driven Development method, writing unit tests using JUnit to create the methods of any complexity inside each class. I divided the tests by which class the methods being tested belonged to and hence I have test classes called DriverTest, FileParserTest, and ReportGeneratorTest. For the tests concerning the methods in FileParser I needed to test files with variable content, so I used a rule that would set up a temporary folder and a technique in which I created and filled a new text document with each unit test which were saved in the temporary folder, used to execute the test, and then discarded. For the tests on the methods in ReportGenerator which had to verify that the generateReport() method was printing the correct output to the console, I temporarily reset the standard system output to a new print stream which I could run tests on using a @Before notation and set it back to the original output stream after the tests were ran using an @After notation. I used the same technique to create temporary files to test on in these tests as I did in the tests on the FileParser tests.

Final Thoughts

I enjoyed this little project quite a bit, actually. It was just challenging enough to be fun and I learned a few things in the process of writing it. I hope it reflects well on my ability to program in Java and my passion for the craft of programming.

About

Text file processor kata for application to Root Insurance.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages