Assignment 4: Abstraction; Constructors
Goals: Learn to abstract common code and use custom constructors.
Instructions
the names of classes,
the names and types of the fields within classes,
the names, types and order of the arguments to the constructor,
the names, types and order of arguments to methods, or
filenames,
Make sure you follow the style guidelines that Bottlenose enforces. For now the most important ones are: using spaces instead of tabs, indenting by 2 characters, following the naming conventions (data type names start with a capital letter, names of fields and methods start with a lower case letter), and having spaces before curly braces.
You will submit this assignment by the deadline using the Bottlenose submission system. You may submit as many times as you wish. Be aware of the fact that close to the deadline the Bottlenose system may slow down to handle many submissions - so try to finish early.
There will be a separate submission for each problem - it makes it easier to grade each problem, and to provide you with the feedback for each problem you work on.
This assignment has only one submission date. You will submit each problem separately in handins by the deadline below:
Due Date: Monday, May 19th, 9:00pm
Practice Problems
Work out these problems on your own. Save them in an electronic portfolio, so you can show them to your instructor, review them before the exam, use them as a reference when working on the homework assignments.
Problems 18.1 - 18.4 on page 225
Problem 18.5 on page 229
Problem 18.6 on page 234
Problem 19.4 on page 263
Problem 1: Abstracting over Data Definitions
In this problem, our data will represent different kinds of drinks you might order at a coffee shop. Each drink has several attributes, some of which are common to them all and some of which differ.
Note: none of these methods are properly implemented. As given in the file, they are all stubs that currently return a dummy value, so the code will compile but not yet work.
Warmup: Download the file and work out the following problems:
Make at least two examples of data for each of the three classes.
Design the isDecaf method for each class. Coffee is never offered decaf; only Rooibos tea is decaffeinated; and milkshakes are always decaffeinated.
Design the method containsIngredient for each class, which determines if a given ingredient has been mixed into the drink.
Design the method format which produces a String showing how the item would appear on a menu. Bubble tea would show the size, then the variety, then the mixins if any in parentheses, such as "24oz Black tea (with boba, milk)" or "16oz Oolong (without mixins)". Commas separate the mixins (but no trailing comma), and if there aren’t any mixins, the format should say "without mixins". Coffees are formatted similarly, as "Hot Arabica americano (with cream, sugar, hazelnut syrup)" or "Iced Robusta demitasse (without mixins)". Milkshakes look like "24oz Haagen-Dasz strawberry (without mixins)" or "32oz JPLicks peach (with sprinkles)".
Once you have finished these methods and are confident that they work properly, save the work you have done to a separate file. Do not submit the code as written so far. The problems below are the main point of this exercise, and it will be helpful for you to preserve the code written so far as a reference against which to compare your revised code below. Again, submit only the work below.
Look at the code and identify all places where the code repeats —
the opportunity for abstraction. Lift the common fields to an abstract class ABeverage, which should implement IBeverage.
Make sure you include a constructor in the abstract class, and change the constructors in the derived classes accordingly. Run the program and make sure all test cases work as before.
For each method that is defined in all three classes decide to which category it belongs:
The method bodies in the different classes are all different, and so the method has to be declared as abstract in the abstract class.
The method bodies are the same in all classes and it can be implemented concretely in the abstract class.
The method bodies are the same for two of the classes, but are different in one class —
therefore we can define the common body in the abstract class and override it in only one derived class.
Hint: You might find that you can abstract more behavior if you design more helper methods...
Now, lift the methods that can be lifted and run all tests again.
Submit your work in a file named Beverages.java.
Problem 2: Working with Custom Constructors, A Look at Equality
When dyers create pigment mixes to dye yarn, they combine amounts (in grams) of red, yellow, blue and black pigments. A perfect recipe for a dye mix ensures the relative amounts of the four colors are appropriate:
Either the weight of black pigment is no more than 5% the weight of the other three colors combined, or the combined weight of the other three colors is no more than 10% the weight of the black pigment. (This is because black is so much darker than the others, so it’s a waste of pigment to have a lot of black and a lot of the other colors at the same time.)
If there’s any yellow at all, then there must be much more yellow than blue: blue must be no more than a tenth of the yellow dye.
Design a DyeRecipe class. The fields should be of type double and represent the weight of the four dye colors in grams. A dye recipe is always scaled such that the total amount of dye is 1g. Provide three constructors for this class:
Your main constructor should take in all of the fields (in the given order) and enforce all above constraints to ensure a perfect dye recipe. When finished, it should contain 1g of dye. Note: To normalize the amounts to 1, divide each amount by the total of the amounts.
Provide another constructor that takes in the amount of red, yellow and blue dyes, and produces the darkest perfect dye recipe it can (by adjusting the weights as needed, while keeping the black dye weight less than the total other dye weight).
Provide another constructor that takes in two other dye recipes, and produces a perfect dye recipe that is a half-and-half mixture of the two input recipes. If there is too much black dye, you should reduce its amount to the maximum allowable amount. (For instance, combining an all-red mixture with an all-blue mixture should produce a purple mixture. Combining an all-red mixture with an all-black mixture should produce a mix with 1/21 g of black, and 20/21 g of red.)
You should use an IllegalArgumentException with a helpful message if the above constraints cannot be enforced.
Remove as much duplicate code as possible from these constructors. (Hint: you may want a Utils class to help with computing or enforcing some of the constraints.)
Implement the method sameRecipe(DyeRecipe other) which returns true if the same ingredients have the same weights to within 0.001 grams.
Submit your work in a file named Dyes.java.