Susan owns a café and hired a programmer to build a system for her baristas to e
ID: 3739491 • Letter: S
Question
Susan owns a café and hired a programmer to build a system for her baristas to enter customer orders and maintain inventory. One of the classes the programmer has written as part of the larger system is Coffee (provided).
You should inspect this class yourself to understand how it works. Note that "cost" refers to the ingredient cost on Susan's behalf, and "price" refers to the price a customer pays for a particular coffee.
After Susan took one look at the class, she fired the programmer and hired you to refactor it. This class uses a lot of bad practice. Most notably, it relies on the usage of strings. For instance, when interfacing with Coffee, a long black has to be constructed like:
Although this works, if one of the strings in the construction were anything but exactly what they are, there would be an error, or worse, the price or cost could be calculated incorrectly. Using enums, we can restrict the parameters to a select few. Susan wrote the class CoffeeEnums to help you get started. In Eclipse, right click CoffeeEnums.java , select Refactor -> Rename..., and change the name to CoffeeFactory (because we will be using the factory pattern later). This tool will automatically update all references, so it's very handy when you want to rename files, variables, methods, etc.
With these enums, we can now change the constructor signature to:
Notice how we are now using Type and Ingredient (the enums in CoffeeFactory). This will break the class and cause a number of compiler errors! Eclipse will suggest automatic fixes to some of these problems that you should activate, such as:
Importing the enums.
Renaming field types to use the enum types.
There will still be some errors remaining. You will need to make the following changes:
Change getPrice() to return the price based on the stored Type. This only needs to be one line!
Change getCost() to return the cost based on the stored Ingredient collection.
You can use the getter method available to you in the enum to radically simplify the summation.
Calculating the cost should be extracted from the constructor to the method getCost(). It's good practice to extract behaviour to singularly purposeful and self-contained methods in order to produce more readable code.
Because the ingredients are not being directly stored as strings, you will need to change listIngredient() while keeping the output in a new-line separated structure. Hint: enums can be converted to strings with toString(), but they will be implicitly casted to strings anyway.
We have now refactored Coffee to use enums, which has made the construction and implementation of the class more logical. Previously, we had to instantiate a Coffee object like so:
Now we can do it like so:
This is better because construction is now restricted to a limited number of items that can be easily extended by Susan. We won't run into any issues with strings this way. There is still some room for improvement. The menu is:
Flat White: one unit of espresso and one unit of milk.
Long Black: two units of espresso.
Mocha: one unit of espresso, one unit of milk, one unit of chocolate.
Rather than specifying both the Type and Ingredient list upon construction, we can use the factory pattern so that we only have to specify the Type, and infer the ingredients from the menu.
Create a new method in the CoffeeFactory class called CreateCoffee. CreateCoffee should take a coffee Type, and return a Coffee object. Use a switch statement to return Coffee objects based on the Type argument, inferring the Ingredient list from the Type. Afterwards, we should be able to easily create coffee objects like so:
The result is a simplified API while maintaining a readable, maintainable, and less error-prone backend that will scale well with future complexity. A GUI could now be developed for the baristas to use. The GUI would then use CoffeeFactory to construct Coffee objects for purposes such as order tracking and inventory management.
Finally, upload and submit your 2 classes:
Coffee
CoffeeFactory
COFFEE.JAVA CODE -
package patt.Coffee;
import java.util.ArrayList;
public class Coffee {
String type;
double cost;
ArrayList<String> ingredients;
public Coffee(ArrayList<String> ingredients, String type) {
this.type = type;
this.ingredients = ingredients;
double sum = 0;
for (String ingredient : ingredients) {
if (ingredient == "espresso") {
sum += 0.5;
} else if (ingredient == "milk") {
sum += 1.0;
} else if (ingredient == "chocolate") {
sum += 1.5;
} else {
sum += 0;
}
}
this.cost = sum;
}
public double getCost() {
return cost;
}
public double getPrice() {
if (this.type.equals("long black")) {
return 4.0;
} else if (this.type.equals("flat white")) {
return 5.0;
} else if (this.type.equals("mocha")) {
return 6.0;
}
return 0;
}
public String listIngredients() {
String string = "";
for (String ingredient : ingredients) {
string += ingredient;
string += " ";
}
return string;
}
}
COFFEEENUMS.JAVA CODE -
package patt.Coffee;
public class CoffeeEnums {
public static enum Type {
LONG_BLACK(4.0),
FLAT_WHITE(4.75),
MOCHA(5.5);
private double price;
Type(double price) {
this.price = price;
}
public double getPrice() {
return price;
}
}
public static enum Ingredient {
ESPRESSO(0.5),
MILK(1),
CHOCOLATE(1.5);
private double cost;
Ingredient(double cost) {
this.cost = cost;
}
public double getCost() {
return cost;
}
}
}
Explanation / Answer
Coffee.java
import java.util.ArrayList;
import coffee.CoffeeFactory.Ingredient;
import coffee.CoffeeFactory.Type;
public class Coffee {
Type type;
double cost;
ArrayList<Ingredient> ingredients;
public Coffee(ArrayList<Ingredient> ingredients, Type type)
{
this.type = type;
this.ingredients = ingredients;
}
public double getCost() {
double sum = 0;
for (Ingredient ingredient : ingredients) {
sum += ingredient.getCost();
}
this.cost = sum;
return cost;
}
public double getPrice() {
return type.getPrice();
}
public String listIngredients() {
String string = "";
for (Ingredient ingredient : ingredients) {
string += ingredient;
string += " ";
}
return string;
}
}
CoffeeFactory.java
import java.util.ArrayList;
public class CoffeeFactory {
public static enum Type {
LONG_BLACK(4.0), FLAT_WHITE(4.75), MOCHA(5.5);
private double price;
Type(double price) {
this.price = price;
}
public double getPrice() {
return price;
}
}
public static enum Ingredient {
ESPRESSO(0.5), MILK(1), CHOCOLATE(1.5);
private double cost;
Ingredient(double cost) {
this.cost = cost;
}
public double getCost() {
return cost;
}
}
public static Coffee createCoffee(Type t) {
ArrayList<Ingredient> ingredients = new ArrayList<CoffeeFactory.Ingredient>();
Coffee coffee;
if (t == Type.FLAT_WHITE) {
ingredients.add(Ingredient.ESPRESSO);
ingredients.add(Ingredient.MILK);
} else if (t == Type.LONG_BLACK) {
ingredients.add(Ingredient.ESPRESSO);
ingredients.add(Ingredient.ESPRESSO);
} else {
ingredients.add(Ingredient.ESPRESSO);
ingredients.add(Ingredient.MILK);
ingredients.add(Ingredient.CHOCOLATE);
}
coffee = new Coffee(ingredients, t);
return coffee;
}
}
Test.java
import java.util.ArrayList;
import coffee.CoffeeFactory.Ingredient;
import coffee.CoffeeFactory.Type;
public class Test {
public static void main(String[] args) {
ArrayList<Ingredient> ingredients = new ArrayList<Ingredient>();
ingredients.add(Ingredient.ESPRESSO);
ingredients.add(Ingredient.MILK);
Coffee coffee = new Coffee(ingredients, Type.FLAT_WHITE);
System.out.println("Flat White coffee:");
System.out.println("Cost: $" + coffee.getCost());
System.out.println("Ingredients: " + coffee.listIngredients());
Coffee longBlack = CoffeeFactory.createCoffee(Type.LONG_BLACK);
System.out.println("Long Black coffee:");
System.out.println("Cost: $" + longBlack.getCost());
System.out.println("Ingredients: " + longBlack.listIngredients());
}
}
Related Questions
Navigate
Integrity-first tutoring: explanations and feedback only — we do not complete graded work. Learn more.