CompSci 308
Spring 2025
Advanced Software Design and Implementation

Lab Coding Exercise : "Advanced" Java Practice

Java has many advanced features that may take some effort to understand how to use well, but that require very little syntax. In some cases, they may not impact your design dramatically (just make your immediate code much cleaner), in others they may help you think about design more deeply to increase your abstraction and flexibility.

In this lab, you will update four old example projects (combined into the lab_advanced repository) to use either Reflection or Lambdas to make the code more flexible letting you more easily hold functionality as values in data structures or data files instead of hardcoding it directly within your code.

In your current project team, complete the following steps:

  1. Setup Pair Programming Environment
  2. Refactorings
  3. Submit Your Work

When completing these exercises, for each numbered step:

Resources

Refactorings

Bins

Using the code in the project's bins package, complete the following refactorings:

  1. Replace a conditional with polymorphism in the Bins class, using Lambdas to easily create the subclasses
    • Change the fitDisksAndPrint() method so that it takes a third parameter of type Consumer<List<Integer>> and replace the entire chain of conditionals with a single call to the parameter's accept() method, passing the correct list variable.
      Note, you may want to cut the conditional code since you will need it later for the body of the methods used in your Lambdas.
    • Within the main() method, change the calls to the fitDisksAndPrint() method to provide this third parameter using Lambdas rather than writing out the complete code to declare and instantiate class instances that implement Consumer<List<Integer>>. Two of the Lambdas will transform the list parameter in some way (e.g., sort, shuffle, or reverse) and one will be empty (i.e., do nothing).
      Note, you should be able to use the code between the braces of each if statement directly, one per call, using the Lambda's arrow, ->, syntax.
  2. Replace separate hardcoded method calls with a loop based on a collection of the actions
    • Replace the three constant strings that describe the WORST_FIT algorithms with a constant Map<String, Consumer<List<Integer>>>, i.e., the description and its associated action.
      Note, it can be immediately initialized using the Map.of() method with the three Lambdas you previously hardcoded in the main() method.
    • Within the main() method, replace the three calls to the fitDisksAndPrint() method with a loop using the constant Map to supply the parameter values for each call instead of the current constants and actions
Roulette

Using the code in the project's game package, complete the following refactorings:

  1. Create a BetFactory class that manages possible Bet subclasses and returns the one chosen by the user so the Game no longer has to manage that responsibility
    • To start, simply move the list of hardcoded subclasses and the promptForBet() method from the Game class to the new BetFactory class
    • Add a public method named makeBet(), that take no parameters and return a Bet superclass, and will be called from the Game class (instead of promptForBet())
  2. Change the hardcoded collection of concrete Bet subclasses to use reflection (note, the available bets will still be hardcoded)
    • Change the List<Bet> to a List<String>, where the strings represent the complete name of each Bet subclass (i.e., the class's package as well as its name).
    • Create a new method makeBetFromString() that gets a Class object representing the Bet subclass named in the given string using the forName() method, then gets its Constructor object, and calls the newInstance() method (and catches its many exceptions) instead of using the instance directly in the previous list.
      Note, you can use either the given default constructor for each Bet subclass or create some logic to provide the description and odds for each subclass — whichever makes more sense for you to practice for your project.
  3. Remove the hardcoded list and replace it with a list of class names from a properties file instead
    • Create a properties file and, in the BetFactory constructor, read it using a ResourceBundle object to determine which subclasses are possible to create so that nothing is hardcoded
      Note, the reflection code itself should likely not need to change because you are using the properties file to populate the List instance variable(s).
      Note, the properties file should map either a general bet name to the complete Bet subclass name (for the default constructor) or the complete Bet subclass name to its odds and description, on one line separated by a comma for easy parsing by your program (for the two argument constructor) — whichever makes more sense for you to practice for your project.
Browser

Using the code in the project's view package, complete the following refactorings (there are no tests yet for the View):

  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.

2D Matrix

Using the code in the project's matrix package, complete the following methods:

  1. Complete the inner class MatrixIterator by implementing Java's expected Iterator methods correctly (you may introduce any instance variables you want to keep track of the current element to return)
    • Implement the hasNext() method to return true only if there is at least one more element available
    • Implement the next() method to return the next sequential element in the matrix (i.e., going to the next row when necessary)

Submitting your Work

At the end of lab, use Gitlab's Merge Request from your forked repository's main branch back to the original organization repository's main branch to submit your group's discussion summary and refactored code.

Make sure the Title of your Merge Request is of the form "lab_advanced_java - everyone's NetIDs".