Design Practice: The Library Example
In this lecture, we are going to do an extended design exercise that practices many concepts from the course so far, and introduces a couple of other small details that are useful as you continue to write Java programs.
Our goal is to design a basic Library system. The library maintains a list of its holdings. Initially, the library has only books. As the exercise goes on, we will expand the library to include other kinds of items. We’ll do this exercise in two stages.
Here is the starter file, containing a basic library class and a Book class. These are very similar to the classes we used in last week’s lecture.
The goal for lecture is less for you to get complete working code, but more for you to practice how to structure classes and where to put methods and fields. Sketching this out on paper would be fine, and would achieve the point of the exercise.
1 Extension 1: Add Items
The library is expanding its holdings to include DVDs and Reference Materials (such as dictionaries – these are materials that do not leave the library). Here are some details about these new items:
The new items, like Books, have a title. All three items have a location in the library, which for books is the call number. DVDs are located at the "Circulation Desk" and Reference materials are on the "Reference Shelf".
DVDs can also be checked out, and hence have the same timesOut and isAvailable fields as Books (and the same checkOut and checkIn methods). Reference materials cannot be checked out and hence shouldn’t have these fields.
Design Task: Add classes and modify the existing code so that the library holdings can contain any of the three kinds of items. You should create additional classes to share fields and methods as appropriate.
Here is an initial version, that contains the basic DVD and Reference classes, but without sharing any fields or methods. You may find it more useful to start from this file (as we will do in Lecture), unless you still want practice defining basic classes.
Here is the version with the class hierarchy corresponding to this task. This was the starter file for class on Tuesday.
2 Extension 2: Add Library Members (Users)
Next we will add members (users/people) to the library. The account for each member stores the member’s name, a list of what items they have checked out, and a double indicating what the member currently owes in late fees (for keeping materials past the due date). The list of items that a member has checked out should be restricted to Books and DVDs (no Reference materials, since they can’t leave the library).
To keep the problem simpler, we are not managing due dates in this exercise. You don’t need to have the due dates as a field in any of your Java classes.
Design Task: Add a class for library members, and figure out how to restrict the checked out items to only allow Books and DVDs.
Design Task: Each of Books and DVDs should have a method lateFee that that takes the number of days overdue a book is and returns the fee for that many days. (Details: for books, the fee is 25 cents a day. For DVDs, the fee is 5 dollars (total) up to 3 days late, then 20 dollars (total) over 3 days.).
The interesting thing here is not the details of writing the lateFee methods, but more how you set up your collection of classes to make sure items that can be checked out have this method.
Here is the version with late fee methods.
Which class(es) should implement IBorrowable? You have two choices: CirculatingItem or each of the Book and DVD classes. In general, having parent/abstract classes implement the interfaces is better, because it saves you having to remember to add the implements annotations on the individual classes. However, if CirculatingItem were NOT abstract (because we wanted to be able to create data from it), then it could not implement the interface because there is no meaningful lateFee method for it (in this case, that is partly because it should be an abstract class, so this reasoning is a bit circular).
3 Extension 3: Let Library Members Borrow and Return Items
Note: We did not cover this in lecture, but it is a good additional practice problem.
Design Task: We want to have a method by which a library member returns a checked-out item. The method needs to take (at least) an integer indicating how many days late the item was returned (it may need other inputs, depending on where you put it). When this method is called, the item should be checked into the library (its isAvailable status set to true), the item should be removed from the member’s list of checked out items, and the member’s fees owed should be updated.
Which class should this method live in? Why?
What inputs does this method need other than the number of days late? What types should those methods have?
Do you need to make any changes to the classes and interfaces we already had to get your methods to compile? Why?
Here is the version with user borrowing and return.
The key thing to note here is that writing these methods required us to add checkIn and checkOut methods (on Book and DVD) to the IBorrowable interface. Try commenting these out and see what happens – what does the compiler complain about? Anytime you call a method inside your code, Java will check that the types you’ve stated for the items guarantees those methods will exist.
4 Java Tidbits
Several little bits of Java come up while developing the actual code. You’ll find these in the files:
In any class, you can write a toString method that changes how Java prints out your objects to the screen. Nothing you need to know, but a good bit of useful Java practice.
The findTitle method returns a special value called null if the title is not found. Ideally, we would raise an error in this case, but we haven’t yet discussed error handling. Returning null (which means "return nothing") is a workaround for the time being.
We added a hasTitle method to the LibItem class, rather than dig into the items using .title. If we had used an interface (such as ILibItem) as the type in the holdings list, instead of the LibItem class itself, Java would not let us access the field directly. Adding the method is how we get around that.