6
\$\begingroup\$

I have been trying learn OOP lately, and have made a simple program that simulates some sort of drive thru experience using a 9 digit keypad to add, delete, and pay for items ordered from the menu.

Everything works as intended, as far as I can tell. I feel like some of my classes may be doing too much, and that overall this program could be simplified.

One of the things I am not sure about is the Customer class, because it only holds the name string. It seems like the customer should be able to do things like pay, etc.

The Order class on the other hand, seems like it's doing too many things.

Please let me know what you think, and what you would have done differently.

Here's the code, and thanks for your time!

Main Class:

/*******************************************************************************
 * Title: Fast Food Simulator
 * Author: Some Dude
 * Date: 7/14/2019
 * Purpose: Learning basic OOP design
 *
 * Description:
 * ! A simple program that simulates placing an order at a drive-thru
 *   using a keypad for input.
 *
 * ! User can add/delete items from their order, and pay for the items
 ******************************************************************************/

package com.company;

import java.util.Scanner;

public class Main
{
    private static Scanner scanner = new Scanner(System.in);

    private static Menu mainMenu = new Menu();
    private static Customer newCustomer;
    private static Order customerOrder = new Order();
    private static Keypad driveThruKeypad = new Keypad();


    /* Main Function Here! */
    public static void main(String[] args)
    {
        CustomerMessages.displayGreeting();
        getCustomerName();
        CustomerMessages.displayKeypadInstructions();
        getOrder();
        customerOrder.payForOrder();
        CustomerMessages.goodbye();
    }


    /***************************************************************************
     *! While the order is active,
     *! Show menu
     *! Display order (if they have actually ordered something)
     *! Get keypad input from customer
     *! Allows user to add/delete items from their order
     **************************************************************************/
    private static void getOrder()
    {
        char itemSelection = '0';

        while (itemSelection != '#')
        {
            mainMenu.displayMenu();

            if (customerOrder.getItemCount() > 0)
                customerOrder.displayOrder(newCustomer.getCustomerName());

            itemSelection = driveThruKeypad.getKeypadEntry();

             if (driveThruKeypad.deleteBtnPressed(itemSelection))
                 deleteItem();
            else
                customerOrder.addItem(mainMenu.getMenuItemName(itemSelection),
                        mainMenu.getMenuItemPrice(itemSelection));
        }
    }


    /***************************************************************************
     *  Prompts user to select an item to delete, then deletes it
     *  only if the item has actually been ordered
     **************************************************************************/
    private static void deleteItem()
    {
        char itemSelection = 0;

        System.out.println("What Item would you like to delete?");

        itemSelection = driveThruKeypad.getKeypadEntry();

        customerOrder.removeItem(mainMenu.getMenuItemName(itemSelection),
                mainMenu.getMenuItemPrice(itemSelection));
    }

    /***************************************************************************
     * Prompts the customer to enter their name, then initializes the Customer
     * instance *newCustomer* using the name given by customer
     **************************************************************************/
    private static void getCustomerName()
    {
        System.out.println("Please enter your name");
        newCustomer = new Customer(scanner.nextLine());
    }

}

Customer Class:

/*******************************************************************************
 * Title: Fast Food Simulator
 * Author: Some Dude
 * Date: 7/14/2019
 * Purpose: Class for a customer, will hold name, and maybe a member id later
 ******************************************************************************/

package com.company;


public class Customer
{
    private String customerName;


    Customer(String name)
    {
        this.customerName = name;
    }


    public String getCustomerName()
    {
        return customerName;
    }

}

Keypad Class

/*******************************************************************************
 * Title: Keypad
 * Author: Some Dude
 * Date: 7/14/2019
 *
 *
 * Description:
 * ! Interface for a 9 digit keypad
 ******************************************************************************/


package com.company;

import java.util.Scanner;

public class Keypad
{

    private Scanner keypadReader = new Scanner(System.in);

    /* Control buttons */
    private final char SUBMIT_BUTTON = '#';
    private final char DELETE_BUTTON = '-';

    /* User defined button ranges */
    private final char minNumberInput = '0';
    private final char maxNumberInput = '9';


    /***************************************************************************
     *! Gets input from user until the button pressed was a valid button
     *  on the keypad (Valid buttons are defined up top)
     **************************************************************************/
    char getKeypadEntry()
    {
        char keypadEntry = '0';
        boolean inputIsValid = false;

        while (!inputIsValid)
        {
            System.out.println("Enter a keypad option: ");
            keypadEntry = keypadReader.next().charAt(0);
            inputIsValid = validateKeypadEntry(keypadEntry);
        }

        return keypadEntry;
    }


    /***************************************************************************
     *! Checks user input to make sure it's valid
     **************************************************************************/
    private boolean validateKeypadEntry(char keypadInput)
    {
        if ((keypadInput >= minNumberInput) && (keypadInput <= maxNumberInput))
            return true;
        else
            return (keypadInput == SUBMIT_BUTTON) || (keypadInput == DELETE_BUTTON);
    }


    /* Checks to see if the '#' button was pressed */
    public boolean orderCompleteBtnPressed(int input)
    {
        return input == SUBMIT_BUTTON;
    }


    /* Checks to see if the '-' button was pressed */
    public boolean deleteBtnPressed(int input)
    {
        return input == DELETE_BUTTON;
    }


}

Order Class:

/*******************************************************************************
 * Title: Order
 * Author: Some Dude
 * Date: 7/14/2019
 * Purpose: Handles everything that has to do with the order
 *
 * Description: Handles everything that has to do with the order such as ->
 *! Adding/deleting order items
 *! Accepting payment for the order
 *! Keeping track of subtotal(no tax), and grandTotal(After tax)
 *! Keeping track of items in the order
 ******************************************************************************/

package com.company;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Scanner;

public class Order
{
    /* Scanner used for keypad input */
    private Scanner scanner = new Scanner(System.in);

    private final double STATE_TAX = 0.10;

    /* Order total before/after tax */
    private double subTotal;
    private double grandTotal;

    /* Counter for items ordered and list to hold item names*/
    private int itemCount;
    private ArrayList<String> orderItems;


    public Order()
    {
        this.itemCount = 0;
        this.subTotal = 0.00;
        this.grandTotal = 0.00;
        this.orderItems = new ArrayList<>();
    }


    /***************************************************************************
     *! Add and item to the order, increment itemCount, increase price
     **************************************************************************/
    void addItem(String itemName, double itemPrice)
    {
        if ((!itemName.equals("")) && (itemPrice > 0.00))
        {
            this.orderItems.add(itemName);
            this.itemCount++;
            this.subTotal += itemPrice;
            System.out.println(itemName + " Added to your order");
            updateGrandTotal();
        }
    }


    /***************************************************************************
     *! If item has actually been ordered, delete it, else, show err message
     **************************************************************************/
    void removeItem(String itemName, double itemPrice)
    {
        if (this.orderItems.contains(itemName))
        {
            this.orderItems.remove(itemName);
            this.itemCount -= 1;
            this.subTotal -= itemPrice;
            System.out.println(itemName + " Deleted from your order");
            updateGrandTotal();
        }
        else
            System.out.println(itemName + " is not in your order");
    }



    double getGrandTotal()
    {
        return this.grandTotal;
    }

    public double getSubTotal()
    {
        return this.subTotal;
    }

    public int getItemCount()
    {
        return itemCount;
    }


    private void setSubTotal(double amount)
    {
        this.subTotal = amount;
    }


    /* Adds the tax rate to the order */
    private void updateGrandTotal()
    {
        this.grandTotal =
                this.subTotal + (this.subTotal * this.STATE_TAX);
    }


    /* Checks to see if item is in orderItems list */
    private boolean findOrderItem(String itemName)
    {
        return this.orderItems.contains(itemName);
    }


    /* Displays the order details to the user */
    public void displayOrder(String customerName)
    {
        System.out.println(
                "************************************************************");
        System.out.printf("%s's order details:\n", customerName);
        System.out.printf("Items ordered: %d\n", itemCount);
        System.out.println(Arrays.toString(this.orderItems.toArray()));
        System.out.printf("Price before tax: $%.2f\n", subTotal);
        System.out.printf("Grand total: $%.2f\n", grandTotal);
        System.out.println(
                "************************************************************");
    }


    /* While order is not paid for, get money from customer */
    public void payForOrder()
    {
        double cashRecieved = 0.00;

        while (grandTotal != 0.00)
        {
            /* Round grandTotal to 2nd decimal place */
            grandTotal = round2Decimals(grandTotal);

            System.out.printf("Your total is $%.2f\n", grandTotal);
            System.out.println("Enter your payment amount");

            cashRecieved = scanner.nextDouble();

            if (cashRecieved > 0)
                grandTotal -= cashRecieved;

            if (grandTotal < 0.00)
                calculateChange();
        }
    }


    /* Calculates the change due (Only used when grandTotal is < 0.00) */
    private void calculateChange()
    {
        double changeDue = 0.00;

        changeDue = (0.00 - grandTotal);
        grandTotal = 0.00;

        changeDue = round2Decimals(changeDue);

        System.out.println("Your change is " + changeDue);
    }


    /* Returns a double rounded to the 2nd value (2.222 = 2.22) */
    private double round2Decimals(double num)
    {
        return (double) Math.round(num * 100) / 100;
    }

}

Customer Messages Class:

/*******************************************************************************
 * Title: Customer Messages
 * Author: Some Dude
 * Date: 7/14/2019
 *
 * Purpose: Stores all of the general messages that the company would
 * want to show on the screen
 ******************************************************************************/

package com.company;

public class CustomerMessages
{

    public static void displayGreeting()
    {
        System.out.println("Welcome to some burger joint!");
    }

    public static void goodbye()
    {
        System.out.println("Thank you for eating at some burger joint!\n" +
                "Please come again!");
    }

    /* Shows the instructions for the keypad */
    public static void displayKeypadInstructions()
    {
        System.out.println("\n");
        System.out.println(
                "************************************************************");
        System.out.println("~Keypad Instructions~");
        System.out.println("To add an item to your order, please press the " +
                "keypad number that matches the item number on the screen");

        System.out.println("To delete an item from your order, press the '-' " +
                "key on the keypad");

        System.out.println("To submit your order, press the '#' key on the " +
                "keypad");
        System.out.println(
                "************************************************************");
    }

}

Menu Class:

/*******************************************************************************
 * Title: Menu
 * Author: Some Dude
 * Date: 7/14/2019
 *
 * Purpose: Interface for the main menu
 *
 * Description:
 * ! Shows the current menu
 * ! Keeps track of how many menu items are available
 * ! Keeps track of menu item prices and names
 ******************************************************************************/

package com.company;

public class Menu
{
    /* Menu Prices */
    private final double CHEESEBURGER_PRICE = 5.25;
    private final double FRENCH_FRIES_PRICE = 2.00;
    private final double DRINK_PRICE = 1.00;

    /* Menu Items */
    private final String CHEESEBURGER = "Cheeseburger";
    private final String FRIES = "Fries";
    private final String DRINK= "Drink";

    /* The amount of items currently on the menu */
    private final int totalMenuItems = 3;


    /* Displays menu options and prices */
    void displayMenu()
    {
        System.out.println("***************************");
        System.out.println("~Menu~");
        System.out.println("1) Cheeseburger $" + CHEESEBURGER_PRICE);
        System.out.println("2) Fries $" + FRENCH_FRIES_PRICE);
        System.out.println("3) Drink $" + DRINK_PRICE);
        System.out.println("***************************");
    }


    public double getCheeseburgerPrice()
    {
        return CHEESEBURGER_PRICE;
    }
    public double getFrenchFriesPrice()
    {
        return FRENCH_FRIES_PRICE;
    }
    public double getDrinkPrice()
    {
        return DRINK_PRICE;
    }
    public int getTotalMenuItems()
    {
        return totalMenuItems;
    }


    /***************************************************************************
     * Returns the name of the item that matches the item selection number,
     * Return blank string if menu item doesn't exist
     **************************************************************************/
    public String getMenuItemName(char itemSelection)
    {
        switch (itemSelection)
        {
            case '1':
                return CHEESEBURGER;
            case '2':
                return FRIES;
            case '3':
                return DRINK;
            default:
                return "";
        }
    }

    /***************************************************************************
     * Returns the price of the item that matches the itemSelection number
     * Return 0.00 if item doesn't exist
     **************************************************************************/
    double getMenuItemPrice(char itemSelection)
    {
        switch (itemSelection)
        {
            case '1':
                return CHEESEBURGER_PRICE;
            case '2':
                return FRENCH_FRIES_PRICE;
            case '3':
                return DRINK_PRICE;
            default:
                return 0.00;
        }
    }

}
\$\endgroup\$
2
\$\begingroup\$
  • On an overall impression, your code looks really clean, and I think that logic separation between your classes is pretty clear.

  • Don't use double with money, as there are precision issues. Consider using BigDecimal. That way you will also avoid using sloppy funtions like round2Decimals.

  • On your Keypad class, you should inject the scanner dependency.

public class Keypad
{

    private Scanner keypadReader;
    public Order(Scanner keypadReader)
    {
        this.itemCount = 0;
        this.subTotal = 0.00;
        this.grandTotal = 0.00;
        this.orderItems = new ArrayList<>();
        this.keypadReader = keypadReader;
    }

    // ...
}
  • Why prepending comments with bang symbols !? This provides no usefulness and should be removed.

  • Use preincrements: this.itemCount -= 1; \$\to\$ --this.itemCount;.

  • Your menu class may be impossible to handle with, let's say, 5 or 6 more ingredients. You may want create a Dish class with a name and a price as attributes.

  • CustomerMessages class should be static.

  • Consider using JavaDoc:

/**
 * @author Some Dude
 * @since 7/14/2019
 *
 * Interface for the main menu:
 * - Shows the current menu
 * - Keeps track of how many menu items are available
 * - Keeps track of menu item prices and names
 */
  • Avoid long /**********... comments. Just /* or /** (for JavaDoc) suffices.

  • Take a look at the Liskov Substitution Principle. Considerable case here is not using ArrayLists for your attributes, but simply Lists:

 private List<String> orderItems; // No longer ArrayList!!!!


    public Order()
    {
        this.itemCount = 0;
        this.subTotal = 0.00;
        this.grandTotal = 0.00;
        this.orderItems = new ArrayList<>();
    }
  • Don't use multi-line comments for single-line comments: /* Menu Items */ \$\to\$ // Menu items

  • Check for static functions in your classes (i.e. void displayMenu() should be static, as well with CHEESEBURGER_PRICE and the other magic numbers/strings).

\$\endgroup\$
  • 1
    \$\begingroup\$ Yes I was thinking the same thing about my round2Decimals() function, it is sloppy. The ! symbols in my comments are just a habit from the programming language I use for my robot automation job, which is similar to C in some ways. I wasn't exactly sure the best practices for java documentation comments, so thanks for pointing that out. Thanks for your time! \$\endgroup\$ – RobotMan Aug 18 at 8:27
0
\$\begingroup\$

Hello, the changes I'm proposing you involves the class Menu , because at the moment every time you modify the menu (adding or deleting a menu item) you have to define a new method to get name and price of the new menuitem or you are obliged to delete methods that uses a deleted item. The solution I propose you to avoid this issues is creating a new class called MenuItem , above the code of the class:

class MenuItem {
    private String name;
    private Double price;

    public MenuItem(String name, Double price) {
        this.name = name;
        this.price = price;
    }

    public String getName() {
        return name;
    }


    public Double getPrice() {
        return price;
    }

    @Override
    public String toString() {
        return String.format("%s $%.2f", name, price);
    }
}

You can check that I override method toString : this method will return a string with price limited to the first two digits like Cheeseburger $5,25. Now the Menu class will contains an ArrayList of MenuItem objects, above the code of the class:

public class Menu {

    private List<MenuItem> items;

    public Menu() {
        this.items = new ArrayList<MenuItem>();
        this.items.add(new MenuItem("Cheeseburger", 5.25));
        this.items.add(new MenuItem("Fries", 2.00));
        this.items.add(new MenuItem("Drink", 1.00));
    }

    public void displayMenu()
    {
        System.out.println("***************************");
        System.out.println("~Menu~");
        int i = 1;
        for (MenuItem item : items) {
            System.out.println(String.format("%d) %s", i++, item));
        }
        System.out.println("***************************");
    }

    public MenuItem getMenuItem(int itemSelection) {
        return items.get(itemSelection);
    }
}

You can see that now you can add or remove elements from the menu. I modified the type of parameter itemSelection from char to int otherwise your menu would be limited to items from 1 to 9. Now the test class code:

public class Main {

public static void main(String[] args) {
    Menu menu = new Menu();
    menu.displayMenu();
    int itemSelection = 1;
    MenuItem item = menu.getMenuItem(--itemSelection); //menu displayed and arraylist differs for one position 
    System.out.println(item); //it will print Cheeseburger $5,25
    System.out.println(item.getName()); //it will print Cheeseburger
    System.out.println(item.getPrice()); //it will print 5.25
}

If the menu starts from 1 , you always have to remember decrementing of one the menuitem selected from the customer to match the arrayList inside the class Menu.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.