Edit

We reached another milestone!

In the past couple of weeks, we learnt all the core concepts underlying OOP!

This REWIND exercise will extend our previous one, using the latest OOP concepts we recently learnt!

Again, get creative and feel free to add more functionality to the system below!


Books ‘r’ Us has become Items ‘r’ Us!

Edit

Our previous BookSeller program has proven to be a hit with the book stores, and they now want to extend their range to include selling DVDs as well as books!

We will build on the OOP skills we developed in the BookSeller exercise, incorporating inheritance and polymorphism.

We are start with a simplified version of the BookSeller program, which only works with Books. We will extend it for Dvds.

Therefore, we are generalising our products as Items, therefore our BookSeller will become ItemSeller.




The required system functionality

We need to develop a system with the following functionality:

  • Maintain the item seller’s cash balance.
  • When the item seller purchases a new item:
    • The cash balance is reduced (by the cost price).
    • The item is added to the collection of items on offer.
    • Give each item a unique ID.
  • Determine the total number of items, of a certain type.




Example usage

This is how we might make use of our system:

public class Main {
   public static void main(String[] args) {
      ItemSeller shop = new ItemSeller("Items 'r' Us", 100.0); 
      System.out.println("Starting balance: $" + shop.getCashBalance()); 
      System.out.println();
      
      Book harryPotter = new Book("Harry Potter Philosopher Stone", "J. K. Rowling", 1997, 10.50, 29.95); // ID: 0
      Dvd shawshank = new Dvd("The Shawshank Redemption", "Frank Darabont", 1994, 8, 22.95);              // ID: 1
      Item godFather = new Dvd("The Godfather", "Francis Ford Coppola", 1972, 11, 29.95);                 // ID: 2
      
      // purchaseStock(Item) demonstrates polymorphism
      shop.purchaseStock(harryPotter);
      shop.purchaseStock(shawshank);
      shop.purchaseStock(godFather);
      
      System.out.println("Balance after purchasing stock: $" + shop.getCashBalance());
      
      System.out.println("Total number of Items: " + shop.getTotalNumberOfItems());
      System.out.println("Total number of DVDs: " + shop.getTotalNumberOfItems(Item.Type.DVD));
      System.out.println("Total number of Books: " + shop.getTotalNumberOfItems(Item.Type.BOOK));
   }
}




Let’s get busy!

You are provide with the following project:


Edit Get your hands dirty!

Edit the above code:

  1. We start with a simple version of the BookSeller and Book classes from our previous REWIND exercise. To focus our attention on the inheritance and polymorphism aspects, we have removed some of the code we implemented last time.
  2. Since the book seller will now be selling other items, we want to generalise it to ItemSeller. Rename BookSeller as ItemSeller in all the places (including the filename BookSeller.java).
    • Hint: if you are using VS Code, you can right-click on the BookSeller.java file and select Rename. Or, simply press F2 when you select the file in the file explorer and type in the new name. If you get a message that tells you it wants to make refactoring changes, press the “OK” button (or press the “Show Preview” button if you want to see what changes will be made).
  3. Create an Item.java file and define the Item class in it.
    • There are some private instance fields in Book that are general enough to also be used for DVDs (and other items). Move them into Item, and remove them from Book. Since we want them to be accessible to derived/children classes (i.e. Book and Dvd), declare them as protected in Item.
    • There are also some public instance methods in Book that are general enough to be inside Item. Move them into Item, and remove them from Book. Keep these methods public.
    • Make the Book class extend the Item class.
    • Create a constructor for Item, that sets the respective instance fields in Item (including id). Your Book constructor calls this constructor (using super(...)) to set the Item instance fields, and updates any Book-only instance fields itself.
    • After all the changes above are made, the code should now compile and run again.
    • The instance field yearPublished has a poor name, since we don’t really say “published” for DVDs and other items. So, let us rename it (and the associated getter) to something more general -– like year (and getYear() instead of getYearPublished()). Similarly, nextBookID isn’t the best name. Change it to nextItemID.
    • So, what did you keep in the Book class? Probably only the author instance field, and the getAuthor() instance method.
  4. Refactor ItemSeller to store and purchase Items (not just Books) in the appropriate places. Check that it all still compiles and runs.
  5. Override the toString() method in Item. It returns a String containing the Item’s details as follows: "Item: <title> (<year>)". Although Book doesn’t override it yet, check that it works with a Book instance.
  6. Override the toString() method in Book, which will override Item’s implementation of toString() as "Book Item: <title> (<year>) <author>". Can you think of an elegant way to re-use Item’s toString() implementation from inside Book’s toString(), so that you don’t completely redo the common bits?
  7. Define a public enum called Type inside the Item class, with BOOK and DVD as possible values.
    • Declare a public Item.Type getType() instance method in Item.
    • Define it in Book, and implement it by returning Type.BOOK as the Type.
    • Does it make sense for Item to provide an implementation of getType()? Probably not, especially we want to avoid users creating instances of Item directly (“item” is rather too general, somewhat abstract). So, make getType() an abstract method, which of course also means making Item now becomes an abstract class also.
  8. Create a Dvd class that extends Item. Similar to Book, it:
    • Inherits some instance fields and methods from Item,
    • Defines getType() and @Overrides toString() declared in Item, and
    • Specialises by introducing a String director instance field and public String getDirector() instance method.
  9. Notice that the public int getTotalNumberOfItems() method inside ItemSeller does not tell us how many items of a particular Type. Add another method: public int getTotalNumberOfItems(Item.Type type), such that it returns the number of items of the specified type.
  10. Optional extensions: Add some of the other features from the last exercise into this program, for example:
    • Selling items: at the Item level,
    • Searching by keyword: at the Item level,
    • Equality: at the Book and Dvd level.