CompSci 307
Fall 2022
Software Design and Implementation

Practice Exercise : Refactoring to use Reflection

This exercise is intended to help you to practice using the "advanced" Java feature reflection: an amazing feature of many modern programming languages that lets you dynamically create objects and call methods based on String values instead of having to know their exact names when the code is compiled. This flexibility lets you more easily hold their values in data structures or data files instead of hardcoding them directly within your code.

Pair Programming

In pairs, switch which person actually writes the code after each numbered step (in industry this switch happens only 1-3 times per day). The person who is not actively coding can be advising, suggesting better names, looking up documentation, or searching the Internet for solutions to small problems you are likely to face, but not multi-tasking (i.e., doing their own work or socializing).

Also after each numbered step, you should be able to run your tests to verify your refactoring did not break any functionality, just improved the design!

Browser: Data Driven GUI

For these refactorings, the only Java code changes need to be made in the browser.view.NanoBrowserView class (other changes will involve making .properties files):

  1. Change the makeButton() method so that it that takes a String parameter instead of an EventHandler (so two different String parameters)
    • For this step, in each call to makeButton(), replace each Lambda that calls a single method with no parameters to just the name of that method (so it is still essentially hardcoded, but now using Strings instead of Lambdas).
    • Change the call to setOnAction() within the makeButton() method to use a new Lambda expression (instead of the one previously passed in) that uses the given string parameter to get a Method object and then call its invoke() method (and catches its many exceptions).
  2. Change the makeButton() method so that it that only takes one String parameter (i.e., no extra parameter telling it what action to perform)
    • For this step create a new properties file that maps the button's key name (the same one used in the original properties file) to the name of the method to invoke when it is pressed.
    • Create a second ResourceBundle instance variable and, in the NanoBrowserView class constructor, read the new properties file so that the method name to call is not hardcoded anywhere.
    • After loading both of the properties files, you should be able to use the one given string to look up both the text for the button to display (from the original ResourceBundle) and the action for the button to perform (from the new ResourceBundle). Again, this reduces the assumptions and what is hardcoded in your program.
  3. Create a data structure to represent the buttons in the Browser's panels
    • For this step create a List<String> instance variable that holds hardcoded values for the buttons' key names used in the properties files (i.e., it will hold the key strings "BackCommand", "NextCommand", and "GoCommand").
    • Change the call to the methods makeInputPanel() in the constructor to take the list of keys.
    • Change the code in the method to use a loop to create the buttons from the given list rather than hardcoding their order and number.

Note, it is typically likely better overall design to make an abstraction (substitutable subclasses) rather than methods, because that is more flexible and recognizable than this simple example. But in either case, reflection can be used to make it data driven rather than hardcoded.