Object Oriented Design and Development

Topic 3: ArrayLists, and Aggregation - Objects within Objects

ArrayLists

In Python you covered lists. We saw that a list was a variable which could be used to hold more than one item, and could be extended to hold new data.

In Java, the ArrayList provides similar functionality to the Python list. We can create an ArrayList and then keep adding objects to it until the JVM's memory runs out. This makes the ArrayList much more flexible than a plain array. Here is an example of code which uses an ArrayList. Imagine we have a Product class:

// Product class
public class Product
{
    private String name;
    private double price;
    private int quantityInStock;
    
    public Product (String nameIn, double priceIn, int quantityIn)
    {
        name = nameIn;
        price = priceIn;
        quantityInStock = quantityIn;
    }
    
    public String toString() 
    {
        return "Name=" + name + " Price=" + price + " Quantity in stock=" + quantityInStock;
    }
}
we could then create an ArrayList of Products as follows:
// Main class
import java.util.ArrayList;

public class ArrayListApp
{
    public static void main (String[] args)
    {
        ArrayList<Product> products = new ArrayList<Product> ();
        
        Product product1 = new Product("Smartphone", 299.99, 5);
        Product product2 = new Product("Tablet", 199.99, 3);
        Product product3 = new Product("SD Card", 9.99, 100);
        
        products.add(product1);
        products.add(product2);
        products.add(product3);
        
        for(int count=0; count<products.size(); count++)
        {
            Product currentProduct = products.get(count);
            System.out.println(currentProduct);
        }
    }
}

A more efficient version of the above code

The code above was written in a way to try and make it as clear as possible what is going on. However, we could write the code more efficiently (less lines of code) as follows:

import java.util.ArrayList;

public class ArrayListExample1
{
    public static void main (String[] args)
    {
        ArrayList<Product> products = new ArrayList<Product> ();
        
        products.add(new Product("Smartphone", 299.99, 5));
        products.add(new Product("Tablet", 199.99, 3));
        products.add(new Product("SD Card", 9.99, 100));
        
        for(int count=0; count<products.size(); count++)
        {
            System.out.println(products.get(count));
        }
    }
}
Note how, rather than creating three variables product1, product2 and product3, we create the product objects and add them to the array list in one statement. This is perfectly acceptable as the add() method of the ArrayList is expecting a Product object as a parameter - and we are giving it one.

Note also how we combine two statements into one in the for loop. Rather than storing the current member of the ArrayList in a variable called currentProduct and then print it directly using the return value of products.get(count). Since products.get(count) gives us a Product object, this is, again, acceptable.

Aggregation

So far we have considered single objects, or objects within arrays or ArrayLists. However, real object-oriented systems typically have many objects interacting with one another. A common scenario is to have objects within other objects and this is called aggregation.

Why is aggregation useful?

Imagine we wanted to write a program to manage stock for a shop. We could write a main() which creates an ArrayList of Product objects (as in the example above) and implement functionality within the main() to search for products, sells products, adds new products and so on. However a more object-oriented approach would be to create a class which represents the shop as a whole. This class could be called Shop. It could contain methods to add a new product, search for a product, or sell a product, and could contain, within it, an ArrayList of Product objects (this would be the aggregation). The advantage of creating a Shop class is that it could be reusable: we could create a Shop class which represents a shop, and then reuse it in many different programs. Also it allows another level of encapsulation: the outside world can use the Shop via its method, without knowing its inner workings.

Example

This example shows the use of a Shop class, as well as a Product class and a test main().

// Product class
public class Product
{
    private String name;
    private double price;
    private int quantityInStock;
    
    public Product (String nameIn, double priceIn, int quantityIn)
    {
        name = nameIn;
        price = priceIn;
        quantityInStock = quantityIn;
    }
    
    public String toString() 
    {
        return "Name=" + name + " Price=" + price + " Quantity in stock=" + quantityInStock;
    }
    
    public boolean sell()
    {
        if(quantityInStock > 0)
        {
            quantityInStock--;
            return true;
        }
        else
        {
            return false;
        }
    }
    
    public String getName()
    {
        return name;
    }
}
// Shop class
import java.util.ArrayList;

public class Shop
{
    private String name;
    private ArrayList<Product> products;
    
    public Shop(String nameIn)
    {
        name = nameIn;
        products = new ArrayList<Product> ();
    }
    
    public void addProduct (Product p)
    {
        products.add(p);
    }

    // Search for a product by name 
    public Product searchForProduct (String searchName)
    {
        for(int count=0; count < products.size(); count++)
        {
            Product currentProduct = products.get(count);
            if(currentProduct.getName().equals(searchName))
            {
                return currentProduct;
            }
        }
        return null;
    }
    
    public boolean sellProduct (String productName)
    {
        Product p = this.searchForProduct (productName);
        if (p != null)
        {
            return p.sell();
        }
        else
        {
            return false;
        }
    }
}
// Test main
public class ShopTestApp
{
    public static void main (String[] args)
    {
        Shop shop = new Shop("Cottage Stores");
        
        shop.addProduct (new Product ("Cheese", 1.99, 10));
        shop.addProduct (new Product("Milk", 0.50, 20));
        shop.addProduct (new Product("Bread", 1.19, 15));
       
        boolean cheeseSellStatus = shop.sellProduct("Cheese"); 
        System.out.println(cheeseSellStatus ? "Cheese sold" : "Cheese could not be sold");
        boolean spamSellStatus = shop.sellProduct("Spam"); 
        System.out.println(spamSellStatus ? "Spam sold" : "Spam could not be sold");
    }
}
How is this working?

Returning boolean status codes to indicate success or failure

You'll also notice in the above code that sell() and sellProduct() do not use a System.out.println to inform the user of success or otherwise. Instead, they return a boolean. This boolean status code is then used in the main() to print an appropriate message.

Why is this done? It's the same reason we used toString() last week. It increases the reusability of our code, in that it can now be used in non-console applications such as web applications or Android apps, where System.out.println() cannot be used. The equivalent of main() in these apps could test the boolean return code and display an appropriate message in an appropriate format. Also it means that different console-mode applications which use our code can display their own message rather than a hard-coded message within Product or Shop.

Also note this code in the main():

boolean cheeseSellStatus = shop.sellProduct("Cheese"); 
System.out.println(cheeseSellStatus ? "Cheese sold" : "Cheese could not be sold");
What is this? It is a concise version of an if statement using the ternary operator (?). It means: if cheeseSellStatus is true, print "Cheese sold", otherwise, print "Cheese could not be sold".

Exercises

  1. ArrayLists: Return to your Student example from previous weeks. In your main(), create a ArrayList of students, and in the loop, add each Student objects to the ArrayList as soon as you create it. Then, when all students have been entered, write a second loop to loop through the array list and display each student in turn.
  2. Aggregation: Create a completely new project (AggregationApp), and add your Student class to it, as well as a main class. Rather like the Shop in the example, create a University class. The University should contain, as an attribute, an ArrayList of Students. The University should include these methods: In your main(), create a University object. Then, develop a simple menu-driven application to add and search for students. The menu should look like this:
    1. Add a student
    2. Search for student by ID
    3. Search for students by name
    4. Quit
    
    You will need a String variable to read in the user's menu choice. Add a while loop which keeps looping until this variable has the value "4". Inside the loop, test which option the user entered, and either: