Search code examples
javaarraysprivateprivate-members

Access to private variables of Objects that are within a private array


I want to create a Restaurant object that can have multiple menus. Each menu can have multiple categories, and each category can have multiple items. Each item will have a name, price, and description.

--Company--
  -Menu ONE-
  -Menu Name-
    -Category ONE-
       -Category Name-
       -Menu Item ONE-
         -Menu Item Name
         -Menu Item Price
         -Menu Item Description
       -Menu Item TWO-
    -Category TWO-
  -Menu TWO- 

Example classes without constructors, getters / setters, or methods.

A company that holds an array of menus

    public class Company
    {   
        // Class constants

        // Class variables
        private String companyName;   //  Stores company name
        private String hours;         //  Restaurant hours
        private String phoneNumber;   //  Restaurant phone number
        private Menu[] menuList;      //  Array of menu objects
        private int menuCount;        //  Number of menus in the array

     }

A menu which holds an array of Categories

    public class Menu
    {   
        // Class constants

        // Class variables
        private String menuName;           //  Name of Menu
        private Category[] categoryList;   //  Array of category objects
        private int categoryCount;         //  Number of categories in the array

     }

A category which holds an array of Menu Items

    public class Category
    {   
        // Class constants

        // Class variables
        private String categoryName;       //  Name of Menu
        private MenuItem[] menItemList;    //  Array of MenuItem objects
        private int menuItemCount;         //  Number of menu items in the array

     }

A single line item with a name, price, and description

    public class MenuItem
    {   
        // Class constants

        // Class variables
        private String name;        //  Name of Menu Item
        private float price;        //  Price of the Menu Item
        private String description; //  Description of the Menu Item

     }

In the driver class, how would I change anything in Menu Item, if I only have access to Company's methods?

I thought about having a getter for each private array of items, but digging down 3 levels into private arrays seems very difficult to implement. It's not as simple as Company.Menu.Category.MenuItem.editName(newName);

Note* I'm not interested in "Should use a Linked list or ArrayList" answers. The program requirements call for basic arrays.

Let me know if I need to be more clear or if you want a SSCCE.


Solution

  • I thought about having a getter for each private array of items, but digging down 3 levels into private arrays seems very difficult to implement. It's not as simple as Company.Menu.Category.MenuItem.editName(newName);

    It is not difficult to implement but it is an anti pattern to invoke successively getters such as :

    company.getMenu().getCategory().getMenuItem().editName(newName);
    

    The Demeter law states as :

    Each unit should have only limited knowledge about other units: only units "closely" related to the current unit.

    Each unit should only talk to its friends; don't talk to strangers.

    Only talk to your immediate friends.

    So to solve your issue, you will have to refactor your design to make collaborate your objects from the one to the other.

    Collaborating/Cascading approach

    You could define a editItemName() method that is successively invoked by each object of the hierarchy.

    Company could define the method such as :

    public void editItemName(String menuName, String categoryName, String itemName, String newName){ 
       Menu menu = findMenu(menuName);
       menu.editItemName(category, item, newName);
    }
    

    Menu could define the method such as :

    public void editItemName(String categoryName, String itemName, String newName){ 
       Category category = findCategory(categoryName);
       category.editItemName(item, newName);
    }
    

    Category could define the method such as :

    public void editItemName(String itemName, String newName){ 
       MenuItem item = findItem(itemName);
       item.editItemName(newName);
    }
    

    And at last MenuItem could define the method such as :

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

    Straighter approach (if suitable)

    Passing so many arguments may be cumbersome for clients. As alternative if Item objects are unique (in terms of equals()) and you can get it from the client side, you could make things still simpler. You could need to define only the method in MenuItem (the leaf) as :

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

    You could define a Map that store the items by their identifier :

    Map<String, MenuItem> mapUniqueItems = new HashMap<>();
    // add items in the map
    mapUniqueItems.put("item1", new MenuItem(..));
    mapUniqueItems.put("item2", new MenuItem(..));
    // add items in the hierarchy as you actually do
    ...
    

    And later you can edit an item from its id, for example :

    String itemIdentifier = ...;
    MenuItem item = mapUniqueItems.get(itemIdentifier);
    item.editItemName("new value");