REWIND #2: Inheritance and Polymorphism
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!
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 Book
s. We will extend it for Dvd
s.
Therefore, we are generalising our products as Item
s, 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:
Get your hands dirty!
Edit the above code:
- We start with a simple version of the
BookSeller
andBook
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. - Since the book seller will now be selling other items, we want to generalise it to
ItemSeller
. RenameBookSeller
asItemSeller
in all the places (including the filenameBookSeller.java
).- Hint: if you are using VS Code, you can right-click on the
BookSeller.java
file and select Rename. Or, simply pressF2
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).
- Hint: if you are using VS Code, you can right-click on the
- Create an
Item.java
file and define theItem
class in it.- There are some
private
instance fields inBook
that are general enough to also be used for DVDs (and other items). Move them intoItem
, and remove them fromBook
. Since we want them to be accessible to derived/children classes (i.e.Book
andDvd
), declare them asprotected
inItem
. - There are also some
public
instance methods inBook
that are general enough to be insideItem
. Move them intoItem
, and remove them fromBook
. Keep these methodspublic
. - Make the
Book
class extend theItem
class. - Create a constructor for
Item
, that sets the respective instance fields inItem
(includingid
). YourBook
constructor calls this constructor (usingsuper(...)
) to set theItem
instance fields, and updates anyBook
-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 -– likeyear
(andgetYear()
instead ofgetYearPublished()
). Similarly,nextBookID
isn’t the best name. Change it tonextItemID
. - So, what did you keep in the
Book
class? Probably only theauthor
instance field, and thegetAuthor()
instance method.
- There are some
- Refactor
ItemSeller
to store and purchaseItem
s (not justBook
s) in the appropriate places. Check that it all still compiles and runs. Override
thetoString()
method inItem
. It returns aString
containing theItem
’s details as follows:"Item: <title> (<year>)"
. AlthoughBook
doesn’t override it yet, check that it works with aBook
instance.Override
thetoString()
method inBook
, which will overrideItem
’s implementation oftoString()
as"Book Item: <title> (<year>) <author>"
. Can you think of an elegant way to re-useItem
’stoString()
implementation from insideBook
’stoString()
, so that you don’t completely redo the common bits?- Define a
public enum
calledType
inside theItem
class, withBOOK
andDVD
as possible values.- Declare a
public Item.Type getType()
instance method inItem
. - Define it in
Book
, and implement it by returningType.BOOK
as theType
. - Does it make sense for
Item
to provide an implementation ofgetType()
? Probably not, especially we want to avoid users creating instances ofItem
directly (“item” is rather too general, somewhatabstract
). So, makegetType()
anabstract
method, which of course also means makingItem
now becomes anabstract class
also.
- Declare a
- Create a
Dvd
class that extendsItem
. Similar toBook
, it:- Inherits some instance fields and methods from
Item
, - Defines
getType()
and@Overrides toString()
declared inItem
, and - Specialises by introducing a
String director
instance field andpublic String getDirector()
instance method.
- Inherits some instance fields and methods from
- Notice that the
public int getTotalNumberOfItems()
method insideItemSeller
does not tell us how many items of a particularType
. Add another method:public int getTotalNumberOfItems(Item.Type type)
, such that it returns the number of items of the specified type. - 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
andDvd
level.
- Selling items: at the