| CompSci 308 Spring 2021 |
Advanced Software Design and Implementation |
Java has many advanced features that may take some effort to understand how to use properly (or even well :) but that require very little syntax. In some cases, they may not impact your design dramatically (just make your code much cleaner) but, in any case, understanding them well can help you think about design more deeply. For example, Reflection is 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. It also makes it easier to provide names for Lambdas and refer to them dynamically instead of having to hardcode their relationship to the code that uses them.
In pairs, complete the following activities today:
lab_advanced into their own repository to allow pushing your group's changes
Settings → Members to add your partner so both people in the group can access the same repositoryMaintainer role in the project and choose Add to Projectgit clone to copy the one, shared, forked repository to their individual machines so they can work on their own separate copiesOpen the cloned folder to create a new project on their personal machineAt the end of lab, use Gitlab's Merge Request from your pair's forked repository's master branch back to the lab's original organization repository's master branch to submit your group's code. You only need to create one Merge Request, no matter how many commits you make, and you do not worry about potential conflicts since your request will not be approved (it is just an easy way for us to see what changes you made).
Make sure the Title of your Merge Request is of the form "lab_advanced - everyone's NetIDs".
For these refactorings, the only Java code changes need to be made in the bins.Bins class:
fitDisksAndPrint() method so that it takes an 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 as the body of the methods used in your Lambdas.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.For these refactorings, the only Java code changes need to be made in the game.roulette.bets.BetFactory class (other changes will involve making .properties files):
Bet subclasses to use reflection (initially a hardcoded list of strings).
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). makeBet() 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. 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.ResourceBundle object to determine which subclasses are possible to create so that nothing is hardcoded anywhere.List instance variable(s).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. 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):
makeButton() method so that it that takes a String parameter instead of an EventHandler (so two different String parameters)
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).setOnAction(), within the makeButton() method, to use your own Lambda (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).makeButton() method so that it that only takes one String parameter (i.e., no extra parameter telling it what action to perform)
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.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.List<List<String>> instance variable that holds hardcoded values for the buttons' key names used in the properties files, with a separate List for each row of buttons (i.e., the first list will hold the button keys "BackCommand", "NextCommand", "HomeCommand", and "GoCommand" and the second list will hold the button keys "AddFavoriteCommand" and "SetHomeCommand"). makePreferencesPanel() and makeNavigationPanel() in the constructor to take the appropriate list of keys.makeXXXPanel() methods are still needed). But it should not be too big a step to see how to make a more comprehensive data structure that allows you the flexibility to create more than just buttons.Note, it is typically likely better overall design to use the Command pattern, which uses 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.