4
\$\begingroup\$

I've been working on a project related to a fruit and vegetable scale program for about a month, and I'm relatively new to programming. In this project, I'm trying to adhere to object-oriented principles and make my code as object-oriented as possible. I started from a single class structure in my previous assignment and now have the freedom to create multiple classes.

I'd appreciate it if experienced developers could review my code for adherence to object-oriented principles and provide constructive feedback on any improvements or areas where I could better follow these principles. I'm particularly interested in whether my use of classes and their interactions align with object-oriented best practices.

Here is a brief overview of my project:

I have implemented various classes to represent different aspects of a fruit and vegetable scale program. The code handles features like adding, browsing, and removing items from the catalog, as well as calculating and displaying the total price for items in the customer's basket. I've also introduced a new PromotionItem class for managing discounted items. The Product class serves as the base class for all products, and PromotionItem is a subclass that inherits from Product. I would be grateful for any feedback, suggestions, or advice you can provide on how to improve the object-oriented structure and design of my code.

Thank you for your time and expertise.

public class Main {
    public static Scanner input = new Scanner(System.in);
    public static boolean runProgram = true;
    public static InputOutput IO = new InputOutput();
    public static Catalog catalog = new Catalog();
    public static Customerbasket customerbasket = new Customerbasket(0);
    public static Menu menu = new Menu();

    public static void main(String[] args) {
        String showIntroToMenu = InputOutput.inputUserString("\nWelcome to the Fruit and Vegetable program." + "\nPlease enter any key to continue to the menu.");
        runCustomerIntroMenu();
        System.out.println("\nThank you for using the Fruit and Vegetable program. \nAll credit goes to Kerem Incorporated.");
    }

    public static void runCustomerIntroMenu() {
        do {
            menu.setMenuOptions(new String[]{"\n1. Search the catalog", "\n2. Browse the catalog." + "\n3. Checkout." + "\n4. Log in as a employee." + "\n5. Exit the program."});
            System.out.println(Arrays.toString(menu.getMenuOptions()));
            System.out.println(Arrays.toString(menu.getMenuOptions()).replace("[", "").replace("]", " "));
            System.out.print("Enter one of the following numbers to continue: ");
            Errormanager.inputMismatchErrorNumber(Main::printCustomerMenuChoices);
        } while (runProgram);
    }

    public static void printCustomerMenuChoices() {
        int employeeInput = input.nextInt();
        input.nextLine();
        switch (employeeInput) {
            case 1 -> searchTheCatalog();
            case 2 -> browseTheCatalog();
            case 3 -> Main.printCustomerBasket();
            case 4 -> logInAsEmployee();
            case 5 -> runProgram = false;
            default -> System.out.println("\nThe input is not valid, please enter a number between 1-5. ");
        }
    }

    public static void runEmployeeIntroMenu() {
        do {
            menu.setMenuOptions(new String[]{"\nWelcome to the Employee Menu." + "\n1. Add a product." + "\n2. Remove a product." + "\n3. Display all products." + "\n4. Log out and return to customer menu." + "\n5. Exit the program."});
            System.out.println(Arrays.toString(menu.getMenuOptions()).replace("[", "").replace("]", " "));
            System.out.print("Enter one of the following numbers to continue: ");
            Errormanager.inputMismatchErrorNumber(Main::printEmployeeMenuChoices);
        } while (runProgram);
    }

    public static void printEmployeeMenuChoices() {
        int employeeInput = input.nextInt();
        input.nextLine();
        switch (employeeInput) {
            case 1 -> createProduct();
            case 2 -> removeProduct();
            case 3 -> System.out.println("to be done!");
            case 4 -> runCustomerIntroMenu();
            case 5 -> runProgram = false;
            default -> System.out.println("\nThe input is not valid, please enter a number between 1-5. ");
        }
    }

    public static void logInAsEmployee() {
        File userNames = new File("usernames.txt");
        File passwords = new File("passwords.txt");
        System.out.println("\nWelcome to the login menu.");
        System.out.print("Please enter username: ");
        String username = input.nextLine();
        System.out.print("Please enter password: ");
        String password = input.nextLine();

        do {
            try {
                Scanner userNamesFile = new Scanner(userNames);
                Scanner userPasswordFile = new Scanner(passwords);
                while (userNamesFile.hasNext() && userPasswordFile.hasNext()) {
                    String userExist = userNamesFile.nextLine();
                    String passExist = userPasswordFile.nextLine();
                    if (userExist.equals(username) && passExist.equals(password)) {
                        runEmployeeIntroMenu();
                    }
                }
                System.out.println("\nWrong username or password.");
            } catch (FileNotFoundException e) {
                System.out.println("File not found");
            }
            String userChoiceToExit = InputOutput.inputUserString("Enter yes to try again or any key to exit to the customermenu: ");
            if (userChoiceToExit.equalsIgnoreCase("Yes")) {
                logInAsEmployee();
            }
            runProgram = false;
        } while (runProgram);
    }

    public static void createProduct() {
        double userSetPrice = 0.0;
        String userSetPricePerPcsOrKg = "";
        String userSetFruitOrVegetable = "";
        String userSetProductType = "";
        InputOutput iO = new InputOutput();
        Product createdUserProduct;
        double userSetDiscount = 0.0;
        int userSetDiscountCondition = 0;

        String userSetName = InputOutput.inputUserString("\nPlease enter the name of the product: ");
        String userChoicePrice = InputOutput.inputUserString("Do you want to add price? Please write yes to or any key to continue: ");
        if (userChoicePrice.equalsIgnoreCase("Yes")) {
            System.out.print("Please enter price: ");
            userSetPrice = input.nextDouble();
            input.nextLine();
            userSetPricePerPcsOrKg = InputOutput.inputUserString("Enter if price is per piece (pcs) or per kilogram (kg). Write pcs or kg: ");
        }
        String userChoiceFruitOrVegetable = InputOutput.inputUserString("Do you want add if product is a fruit or vegetable? Please write yes or any key to continue: ");
        if (userChoiceFruitOrVegetable.equalsIgnoreCase("Yes")) {
            userSetFruitOrVegetable = InputOutput.inputUserString("Please enter if product is a fruit or vegetable: ");
        } else {
            userSetFruitOrVegetable = ("Field is unassigned");
        }
        String userChoiceProductType = InputOutput.inputUserString("Do you want to add the producttype? Please write yes or any key to continue: ");
        if (userChoiceProductType.equalsIgnoreCase("Yes")) {
            userSetProductType = InputOutput.inputUserString("Please enter the producttype: ");

        } else {
            userSetProductType = ("Field is unassigned");
        }

        String userChoiceDiscountCondition = InputOutput.inputUserString("Do you want to add a discount condition to the product? Please write yes or any key to continue: ");
        if (userChoiceDiscountCondition.equalsIgnoreCase("Yes")) {
            System.out.print("Please enter discount condition: ");
            userSetDiscountCondition = input.nextInt();
            input.nextLine();
        }

        String userChoiceSetDiscount = InputOutput.inputUserString("Do you want to add a discount to the product? Please write yes or any key to continue: ");
        if (userChoiceSetDiscount.equalsIgnoreCase("Yes")) {
            System.out.print("Please enter the discount (as a decimal, e.g., 0.1 for 10%): ");
            userSetDiscount = input.nextDouble();
            input.nextLine();
        }

        if (userSetDiscount == 0.0) {
            createdUserProduct = new Product(userSetName, userSetPrice, userSetPricePerPcsOrKg, userSetFruitOrVegetable, userSetProductType);
        } else {
            createdUserProduct = new DiscountedProduct(userSetName, userSetPrice, userSetPricePerPcsOrKg, userSetFruitOrVegetable, userSetProductType, userSetDiscount, userSetDiscountCondition);
        }
        System.out.println("\n\033[1mYou added: \033[0m" + createdUserProduct);
        catalog.setAllFruitsandVegetables(createdUserProduct);
    }

    public static void searchTheCatalog() {
        boolean runSearchCatalog = false;
        Customerbasket customerbasket = new Customerbasket(0);

        do {
            String searchTerm = InputOutput.inputUserString("\nPlease enter a keyword: ").trim();
            if (searchTerm.isEmpty()) {
                System.out.println("\nYou cannot leave it blank, please try again.");
                return;
            }

            boolean resultFoundInCatalog = false;

            for (Product tempCatalog : catalog.getAllFruitsandVegetables()) {
                if (tempCatalog.toString().toLowerCase().contains(searchTerm.toLowerCase())) {
                    System.out.println("\n\033[1mThe result of your search:\033[0m");
                    System.out.println(tempCatalog.toString());
                    String userChoiceYesOrNo = InputOutput.inputUserString("\nDo you want to calculate price for the product?" + "\nPlease enter yes to accept or any key to decline: ");
                    if (userChoiceYesOrNo.equalsIgnoreCase("yes")) {
                        System.out.print("Please enter quantity: ");
                        double userChoiceQuantity = input.nextDouble();
                        input.nextLine();
                        calculatePrice(userChoiceQuantity, tempCatalog.getPrice(), tempCatalog);
                    }
                    Main.addToCustomerBasket();
                    if (userChoiceYesOrNo.equalsIgnoreCase("yes")) {
                        Main.addToCustomerBasket();
                    }

                    resultFoundInCatalog = true;
                }
            }
            if (!resultFoundInCatalog) {
                System.out.println("\nNo search results found.");
            }
            String exitSearchCatalog = InputOutput.inputUserString("Do you want to exit to the main menu? Write Yes to exit or any key to try again: ");
            if (exitSearchCatalog.equalsIgnoreCase("Yes")) {
                runSearchCatalog = true;
            }

        } while (!runSearchCatalog);
    }

    public static void removeProduct() {
        boolean runRemoveProduct = false;

        do {
            boolean doesProductExist = false;
            String userChoice = InputOutput.inputUserString("\nPlease enter the name of the product you want to remove: ");
            for (int i = 0; i < catalog.getAllFruitsandVegetables().size(); i++) {
                if (catalog.getAllFruitsandVegetables().get(i).getName().equalsIgnoreCase(userChoice)) {
                    Product removedProduct = catalog.getAllFruitsandVegetables().remove(i);
                    System.out.println("You removed " + removedProduct.getName() + ".");
                    doesProductExist = true;
                }
            }
            if (!doesProductExist) {
                System.out.println("\nThis product does not exist.");
            }
            String userChoiceToExit = InputOutput.inputUserString("Type yes to exit or any key to begin again: ");
            if (userChoiceToExit.equalsIgnoreCase("Yes")) {
                runRemoveProduct = true;
            }

        } while (!runRemoveProduct);


    }

    public static void calculatePrice(double quantity, double price, Product thisProduct) {
        double sum = quantity * price;
        System.out.println("The total price will be: " + sum + " kr." + " (" + price + " kr/" + thisProduct.getPricePerPcsOrKg() + ")");
    }

    public static void browseTheCatalog() {
        boolean quitCatalog = false;
        do {
            menu.setMenuOptions(new String[]{"\nWelcome to the product catalog, do you want to:" + "\n1. Browse the catalog." + "\n2. Search the catalog." + "\n3. Go back to the main menu."});
            System.out.println(Arrays.toString(menu.getMenuOptions()).replace("[", "").replace("]", " "));
            System.out.print("Please enter a number: ");
            int userCatalogChoice = input.nextInt();
            input.nextLine();
            if (userCatalogChoice == 1) {
                findUniquePrimaryTypeInCatalog();
                System.out.print("Please choose from the options: ");
                findUniqueProductTypeInCatalog();
                System.out.print("Please choose from the options: ");
                printFullItemFromInputFromCatalog();
            } else if (userCatalogChoice == 2) {
                searchTheCatalog();
            } else if (userCatalogChoice == 3) {
                quitCatalog = true;
            }
        } while (!quitCatalog);
    }

    public static void findUniquePrimaryTypeInCatalog() {
        for (Product uniqueItem : catalog.getAllFruitsandVegetables()) {
            String templist = uniqueItem.getFruitOrVegetable();
            catalog.getSetofPrimaryTypes().add(templist.toLowerCase());
            for (Iterator<String> i = catalog.getSetofPrimaryTypes().iterator(); i.hasNext(); ) {
                String element = i.next();
                if (element.equalsIgnoreCase("Field is Unassigned")) {
                    i.remove();
                }
            }
        }
        System.out.println(catalog.getSetofPrimaryTypes());

    }

    public static void findUniqueProductTypeInCatalog() {
        String firstUserCatalogChoice = input.nextLine().toLowerCase();
        for (Product uniqueItem : catalog.getAllFruitsandVegetables()) {
            if (uniqueItem.getFruitOrVegetable().toLowerCase().contains(firstUserCatalogChoice)) {
                String templist = uniqueItem.getProductType();
                catalog.getSetOfProductTypes().add(templist.toLowerCase());
            }
        }
        System.out.println(catalog.getSetOfProductTypes());
    }

    public static void printFullItemFromInputFromCatalog() {
        String firstUserCatalogChoice = input.nextLine().toLowerCase();
        if (catalog.getSetOfProductTypes().contains(firstUserCatalogChoice.toLowerCase())) {
            System.out.println("\n\033[1mFull products: \033[0m");
            for (Product fullProduct : catalog.getAllFruitsandVegetables()) {
                if (fullProduct.getProductType().equalsIgnoreCase(firstUserCatalogChoice.toLowerCase())) {
                    System.out.println(fullProduct);
                    System.out.println();
                }
            }
        }
    }

    public static void addToCustomerBasket() {
        String userChoiceYesOrNo = InputOutput.inputUserString("\nDo you want to add the product to the customerbasket?" + "\nPlease type yes to add product to basket or any key to continue: ");
        if (userChoiceYesOrNo.equalsIgnoreCase("yes")) {
            String userEnterProduct = InputOutput.inputUserString("Please enter the full name of the product to add to basket: ");
            System.out.print("How much or how many do you want to add to basket, please enter the quantity: ");
            int userEnterQuantity = input.nextInt();
            input.nextLine();
            for (Product tempBasket : catalog.getAllFruitsandVegetables()) {
                if (tempBasket.getName().equalsIgnoreCase(userEnterProduct)) {
                    if (tempBasket instanceof DiscountedProduct && ((DiscountedProduct) tempBasket).getDiscountCondition() == userEnterQuantity) {
                        customerbasket.getAllItemsInCustomerBasket().add((tempBasket.getPrice() * userEnterQuantity) * ((DiscountedProduct) tempBasket).getDiscount());
                        customerbasket.setItemsInBasket(userEnterQuantity);
                    } else {
                        customerbasket.getAllItemsInCustomerBasket().add((tempBasket.getPrice() * userEnterQuantity));
                        customerbasket.setItemsInBasket(userEnterQuantity);
                    }
                }
            }
        }
    }

    public static void printCustomerBasket() {
        double fullPrice = 0;
        for (Double list : customerbasket.getAllItemsInCustomerBasket()) {
            fullPrice += list;
        }
        System.out.println("\nWelcome to the checkout.");
        System.out.println("Your basket contains " + customerbasket.getItemsInBasket() + " items and the total price is " + fullPrice + " kr.");
    }
}

public class DiscountedProduct extends Product {
    private double discount;
    private int discountCondition;

    public DiscountedProduct(String name, double price, String pricePerPcsOrKg, String fruitOrVegetable, String productType, double discount, int discountCondition) {
        super(name, price, pricePerPcsOrKg, fruitOrVegetable, productType);
        this.discount = discount;
        this.discountCondition = discountCondition;
    }

    public double getDiscount() {
        return discount;
    }

    public void setDiscount(double discount) {
        this.discount = discount;
    }

    public int getDiscountCondition() {
        return discountCondition;
    }

    public void setDiscountCondition(int discountCondition) {
        this.discountCondition = discountCondition;
    }

    @Override
    public String toString() {
        return super.toString() + "\nDiscount: " + "Buy " + discountCondition + " for " + (discount * 100) + "% " + "off";
    }
}
import java.util.ArrayList;
import java.util.HashSet;

public class Catalog {
    private ArrayList<Product> allFruitsandVegetables;
    private HashSet<String> setofPrimaryTypes;
    private HashSet<String> setOfProductTypes;

    public Catalog() {
        this.allFruitsandVegetables = new ArrayList<>();
        this.setofPrimaryTypes = new HashSet<>();
        this.setOfProductTypes =  new HashSet<>();
    }

    public ArrayList<Product> getAllFruitsandVegetables() {
        return allFruitsandVegetables;
    }

    public void setAllFruitsandVegetables(Product product) {
        this.allFruitsandVegetables.add(product);
    }

    public HashSet<String> getSetofPrimaryTypes() {
        return setofPrimaryTypes;
    }

    public void setSetofPrimaryTypes(HashSet<String> setofPrimaryTypes) {
        this.setofPrimaryTypes = setofPrimaryTypes;
    }

    public HashSet<String> getSetOfProductTypes() {
        return setOfProductTypes;
    }

    public void setSetOfProductTypes(HashSet<String> setOfProductTypes) {
        this.setOfProductTypes = setOfProductTypes;
    }
}
import java.util.ArrayList;
import java.util.HashSet;

public class Catalog {
    private ArrayList<Product> allFruitsandVegetables;
    private HashSet<String> setofPrimaryTypes;
    private HashSet<String> setOfProductTypes;

    public Catalog() {
        this.allFruitsandVegetables = new ArrayList<>();
        this.setofPrimaryTypes = new HashSet<>();
        this.setOfProductTypes =  new HashSet<>();
    }

    public ArrayList<Product> getAllFruitsandVegetables() {
        return allFruitsandVegetables;
    }

    public void setAllFruitsandVegetables(Product product) {
        this.allFruitsandVegetables.add(product);
    }

    public HashSet<String> getSetofPrimaryTypes() {
        return setofPrimaryTypes;
    }

    public void setSetofPrimaryTypes(HashSet<String> setofPrimaryTypes) {
        this.setofPrimaryTypes = setofPrimaryTypes;
    }

    public HashSet<String> getSetOfProductTypes() {
        return setOfProductTypes;
    }

    public void setSetOfProductTypes(HashSet<String> setOfProductTypes) {
        this.setOfProductTypes = setOfProductTypes;
    }
}
import java.util.Arrays;

public class Menu {
    private String[] menuOptions;
    public Menu() {
        this.menuOptions = new String[10];
    }
    public String[] getMenuOptions() {
        return menuOptions;
    }
    public void setMenuOptions(String[] menuOptions) {
        this.menuOptions = menuOptions;
    }

}


import javax.sound.midi.Soundbank;
import java.util.Scanner;

public class InputOutput {
    private static Scanner input;
    public InputOutput(){
        this.input = new Scanner(System.in);
    }

    public static String inputUserString(String messageToUser) {
        System.out.print(messageToUser);
        return input.nextLine();
    }
}
import java.util.InputMismatchException;

public class Errormanager {
    public static void errormanager (String[] args){
    }

    public static void inputMismatchErrorNumber (Runnable method){
        try {
            method.run();
        } catch (InputMismatchException e) {
            String errorMessage = InputOutput.inputUserString("\nThe input is not valid, you cannot enter a letter, please enter a number.");
        }
    }

}import java.util.ArrayList;
import java.util.Scanner;

public class Customerbasket {
    private ArrayList<Double> allItemsInCustomerBasket;
    private int itemsInBasket;
    public static Scanner input = new Scanner(System.in);

    public Customerbasket(int itemsInBasket) {
        this.allItemsInCustomerBasket = new ArrayList<>();
        this.itemsInBasket = itemsInBasket;
    }

    public ArrayList<Double> getAllItemsInCustomerBasket() {
        return allItemsInCustomerBasket;
    }

    public void setAllItemsInCustomerBasket(ArrayList<Double> allItemsInCustomerBasket) {
        this.allItemsInCustomerBasket = allItemsInCustomerBasket;
    }

    public double getItemsInBasket() {
        return itemsInBasket;
    }

    public void setItemsInBasket(int userQuantity) {
        itemsInBasket += userQuantity;
    }

}
/*
Namn: Kerem Tazedal
Mejl: [email protected]
 */
public class Product {
    private String name;
    private double price;
    private String pricePerPcsOrKg;
    private String fruitOrVegetable;
    private String productType;

    public Product(String name, double price, String pricePerPcsOrKg, String fruitOrVegetable, String productType) {
        this.name = name;
        this.price = price;
        this.pricePerPcsOrKg = pricePerPcsOrKg;
        this.fruitOrVegetable = fruitOrVegetable;
        this.productType = productType;
    }
    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public double getPrice() {
        return price;
    }

    public void setPrice(double price) {
        this.price = price;
    }

    public String getPricePerPcsOrKg() {
        return pricePerPcsOrKg;
    }

    public void userSetPricePerPcsOrKg(String pricePerPcsOrKg) {
        this.pricePerPcsOrKg = pricePerPcsOrKg;
    }

    public String getFruitOrVegetable() {
        return fruitOrVegetable;
    }

    public void setFruitOrVegetable(String fruitOrVegetable) {
        this.fruitOrVegetable = fruitOrVegetable;
    }

    public String getProductType() {
        return productType;
    }

    public void setProductType(String productType) {
        this.productType = productType;
    }

    @Override
    public String toString() {
        return "\nName of the product: " + name + "\nPrice: " + price + " kr per " + pricePerPcsOrKg + "\nPrimarytype: " + fruitOrVegetable + "\nSecondarytype: " + productType;
    }

}
\$\endgroup\$
3
  • \$\begingroup\$ What were the OO principles you have tried to adhere to? You should add them to the question. It makes the review easier. At the moment it is a very open ended question. \$\endgroup\$ Commented Nov 2, 2023 at 12:06
  • 1
    \$\begingroup\$ This is a continuation of codereview.stackexchange.com/questions/287339/… \$\endgroup\$
    – Reinderien
    Commented Nov 2, 2023 at 12:50
  • \$\begingroup\$ there are two Catalog classes. \$\endgroup\$
    – radarbob
    Commented Nov 17, 2023 at 23:03

2 Answers 2

3
\$\begingroup\$

Warning about inheritance

I saw your implementation of the DiscountedProduct. I would like to warn you for inheritance as taught at school or in courses. It is stressed too much. (I got in trouble during (the first try) of my graduation project because how they teached it.)

Inheritance should be thought of as a technique that should rarely be used (e.g. like reflection). Problems you can encounter by overuse of inheritance (at the top of my mind):

  1. With inheritance code that you use and understand as a whole can become spread out over multiple files.

    Here: The logic for determining the price is in two classes. Imagine you have to implement a Christmas discount for specific products - different from the distinction DiscountProduct or Product.

    My advise: Make sure what you are doing is more separation of concerns than splitting up the same concern :-)!

  2. Inheritance requires the code to be separated based on only one distictive criterion in your functionality. This can only be one distinction. Other distinctions will have to be made in another way or with doubling the number of classes in the hierarchy. This will make the code more confusing.

    Here: The criterion to split up the hierarchy is eligability for discount. It is not obvious to me, because it does only affect a small portion of the logic. Similar distinctions like pricePerKg/pricePerPiece are handled in a different way. This makes it more difficult to reason about (It feels like coding in multiple dimensions :-).)

    Example of a good criterion: I once coded a timeline with events: some events happen at a specific moment in time others for a period. There was quite some functionality and some was the same and some was different. This distinction was fundamental to the UI element. It affected most of the logic. Inheritance worked.

    My advise: in rare case you use inheritance, have an obvious and fundamental reason of division. Else it will make the code more confusing.

  3. When using inheritance switching the type of your object at run time is complex.

    Here: Imagine you want to be able to a add a discount for a product. Will your Product be replaced by a DiscountedProduct? Then you'll have to create a new DiscountedProduct object and copy properties. Which references will have to be updated?

    My advise: only use inheritance when switching type is not plausible.

  4. Inheritance often requires casting, which can cause bugs not be seen at compile time.

Improvements

Discount

You don't need both a Product and DiscountedProduct class. A discount set to 0 as a default works as well. O, see how much you can simplify now?

Final price

The code for calculating the total price is now inside the console code. I didn't look there. You can better put it inside the Product class or a PriceCalculator class. (Since it's not much and is related to a product, I would suggest the Product class.)

Your code is good for having one month of experience. I hope you learn from my lessons.

Best practices

  • "Prefer composition over inheritance"
  • "Prefer interfaces over abstract/super classes"
  • KISS: Keep it simple, smarty!
  • A (valuable) programming coin reads "Separation of concerns" on its heads. "Bundling the same concern" is on its tails.*
  • Prefer compile time errors over runtime exceptions.

*Is there an "official" phrase?

\$\endgroup\$
3
  • \$\begingroup\$ Thank you very much, will try to learn and work hard! \$\endgroup\$ Commented Nov 11, 2023 at 9:48
  • \$\begingroup\$ You're welcome. Learn and practice (and take a break :-) ). I also discovered programming is a lot more about being guided by intuition of what seems a practical approach than being guided by "hard coded" best practices. You will after a while truly grasp the best practices from your own experiences. Give yourself time to build up your experience. \$\endgroup\$
    – pikachu
    Commented Nov 15, 2023 at 13:41
  • \$\begingroup\$ "Prefer interfaces over abstract/super classes` => No. These are very different: An interface only declares public members (ie. method signatures); that's all. The primary purpose, IMO, is so unrelated objects (no inheritance relationship) have polymorphic behavior - Cats & Parrots barking like a Dog let's say. abstract class has that API plus overridable implementation, base implementation, optional implementation, and mandatory implementation. Composition polymorphism is there too. Together these guarantee complex subclass customization integrated with consistent base behavior. \$\endgroup\$
    – radarbob
    Commented Nov 17, 2023 at 23:46
4
\$\begingroup\$

inconsistent data structure

use the command design pattern to create your menus. By appling this you

  • bring consistency to your code, since the mapping between menu String and input mapping is broken up
  • lets you easily create, change and update commands without touching unrelated code (which is also know as OpenClosed principle, since its open for changes as described, but you close the program against manipulation. Closed for manipulation means: "not touching unrelated code")
  • the display will be much easier since you only have to print those actions

Responsibility: print

nobody is responsible for that job in your code. Make someone responsible! create a class MenuPrinter and address all the printing to it. (this also applies the Single Responsibility pattern, aka the S in SOLID.

Separation of concerns I

Break up the concerns pricing and product. they are only loose coupled. make your code represent that!

class Product {
    private final double basePrice; 
}

class PriceCalculator {
    public double calculateTotal(Basket basket);
    public double calculatePrice(Product product);//handles discount etc.
} 

Separation of Concerns II

your Catalog does not unfold its full potential. you could move all the product-related methods (createProduct(), removeProduct(), searchTheCatalog() and many more) into the catalog.

you should then remove the Inputhandling from the catalog functionality.

Responsibility Inputhandling

InputHandling should be addressed to some kind of Handler. Right now all your Input handling is widespread through your code. that must be merged into a proper class.

Summary

its nice to see so much improvements on your code and i really hope that you keep up that spirit - that is what makes you a better programmer!

I was not able to name all code issues on your current project (eg. unrequired Import: javax.sound.midi.Soundbank;), i focused only on the major issues. I hope to see your improvements on them and then we shall talk again ;-)

\$\endgroup\$
2
  • 1
    \$\begingroup\$ Thank you for the amazing feedback, i am trying my best and hope to improve further! A You deserve a large gold star! \$\endgroup\$ Commented Nov 9, 2023 at 15:36
  • 1
    \$\begingroup\$ i saw your progress so far and i am looking forward to see more of your stuff :-) happy Coding @KeremTazedal \$\endgroup\$ Commented Nov 10, 2023 at 7:29

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

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