Object-oriented programming with Java
Editor
Output
Designing Classes
Overview
A class represents the blueprint for the state and behaviour of objects which derive from it.
This task is designed to help you develop a class to represent a library book (or more accurately a library book record) whereby a book can be loaned, renewed, returned and have a hold placed upon it.
In order to carry out operations like checking the book out or renewing it, the class will also need to be aware of the length of a loan and how many times the book can be renewed, as these values might vary from book to book.
It will also make use of the LocalDate class for handling date related operations (like storing when the book is due to be returned).
All the code you write should go in the editor below, and with the sole exception of any 'import' statements, the code should be placed between the existing braces, that is between { and }
Adding some fields
Although a library book might have various bits of data associated with it, such as title, publisher, edition, publication date, ISBN etc., we will keep our implementation of the 'book' part simple and use just store its title. This will be stored in a field which is a variable that belongs to each object derived from the class. Another name for a field, more commonly used in other programming languages, is an instance variable or ivar.
-
Add the following code, inside the class to create a String field called title:
Note the use of theprivate String title;privatekeyword. This ensures that the title field is not accessible from outside the class. - Run this test and ensure it passes. If it does not, check the output message for feedback. The first time you run at test it may take 15 seconds or more, but subsequent runs shoud be much quicker
- Add another field named
loanLengthwhich will store an integer value - Run this test, and check it passes, referring to any feedback in the output window if it fails
- Add another field named maxNumberOfRenewals, again to store an integer and once you have done this, run the test.
Creating a constructor
Our LibraryBook class has fields for name, loanLength and maxNumberOfRenewals, but they have not been assigned any values (and, as different books have different titles, loan lengths and limits on how many times they can be renewed, we do't want them to have default values) however we want to ensure that those fields contain the correct values when a new LibraryBook is created (rather than being left as null). To do this we need to create a constructor. A constructor is used when we create a new instance of a class to configure it ready for use. For our LibraryBook we might use a constructor as follows:
//do not add this code to your project
int daysPerLoan = 7;
int maxRenewals = 5;
LibraryBook warAndPeace = new LibraryBook("War and Peace", daysPerLoan, maxRenewals);
-
Add the following, partially complete code to create a constructor:
You will notice that there is a parameter calledpublic LibraryBook(String title, int loanLength, int maxNumberOfRenewals) { this.title = title; //todo: set loanLength //todo: set maxNumberOfRenewals }titlein the constructor which has the same name as one of the fields we created earlier. To differentiate between the field and the parameter with the same name, we prefix the name of the field with the keywordthis. The linethis.title = title;takes the value from the parameter and stores it in the field - Run the tests to verify this has been added correctly
- Neither the
loanLengthor themaxNumberOfRenewalsparameters are stored in the field by the constructor. Add code in place of the comments to set these fields - Run the test and verify that it passes
- Finally, once the LibraryBook is created and has a value set by the constructor, we want to ensure the title cannot be changed. Add the keyword
finalso the title field appears as followsprivate final String title; - Run the test to ensure that the field is correctly specified as final
Accessors
Each of our fields are private (which is normally how fields should be), however we will want to be able to access each of the fields we've added so far from outside the class. We will add accessors or 'getters', which are methods which retrive the value of a field. These methods are declared public so they can be accessed from anywhere else
The convention for naming a getter is the word get followed by the name of the field, but where the first character of the first word of the field name is uppercase
-
Add the following code to create a getter for the
titlefield:public String getTitle() { return title; } - Run the test
-
Add the following code to create a getter for the
loanLengthfield:public int getLoanLength() { return loanLength; } - Run the test
- Finally, using the same conventions as above, add a getter for the
maxNumberOfRenewalsfield - Run the test
Mutators
It should be possible to change the value in some of the fields after an instance of LibraryBook has been created. In particular the loanLength and maxNumberOfRenewals fields. To do this we need to create a mutator (or setter). Like getters, these are methods which belong to the class and are declared as public
The convention for naming most setters is the word set followed by the field name, but where the first character of the first word of the field name is uppercase
-
Add a public method called
setLoanLengthwhich will take an int as a parameter, and store it in theloanLengthfield as shown here:public void setLoanLength(int loanLength) { this.loanLength = loanLength; } - Run the test
- Add another method named
setMaxNumberOfRenewalswhich also takes an int as a parameter and stores it in themaxNumberOfRenewalsfield - Run the test
Additional fields for the class
In order for our class to function as desired, it needs to have certain additional fields to keep track of the state of loans. This includes - how many times the book has been renewed, when the book is due back, and whether someone has placed a hold on the book (i.e. to prevent it from being renewed again)
- Create a field named
currentRenewalswhich should store an int and have a default value of 0 - Run the test and verify this has been done correctly
- Create another field to hold a boolean named
heldand set its initial value to false - Run the and verify that this has been created correctly
- Finally create a variable named
dueDatewith the typeLocalDatebut set its initial value tonullThe reason the initial value is set to null in this case, is because a book that is not on loan will not have a due date.Note that you will also need to add an import statement for the LocalDate class as follows:
import java.time.LocalDate; - Run the test to verify this has been done correctly
Additional getter
- Using the same approach and naming convention used for getters previously, add a getter for the
heldfield. Note that convention allows getters for boolean values to be named using the prefix 'is' or 'has' rather than 'get', so name this methodisHeld - Run the test
Additional methods for the class
Check out
-
We'll start by adding method to try to check out the book. This will be one of the more complex methods as we need to first check the book is not already on loan (by checking the
dueDatefield isnull, and if so setdueDateso that isloanLengthdays in the future, by adding that many days to the current date and then returningtrue. If however thedueDatefield is notnull, the book must already be on loan, so we will returnfalse(to indicate the checkOut was unsuccessful)Add the following method which does what was described above:
public boolean checkOut(){ if (dueDate == null){ dueDate = LocalDate.now().plusDays(loanLength); return true; } return false; } - Now the checkout process is implemented, add a getter for the
dueDatefield - Run the test
- Run the test
On loan
- Add a method named
onLoanwhich will check if a book is on loan (i.e. by verifying the dueDate is not null) and return true or false accordingly - Verify the solution is correct by running the test
Check in
- Checking a book back in is straightforward, we set the currentRenewals field's value to 0 and the dueDate field's value back to null. Add a method called
checkInto do that - Run the test called test
Placing a hold
- It should only be possible to place a hold if a book is on loan, and not already being held. Add a method called
placeHoldto set theheldfield to true if the appropriate conditions are met, and then return eithertrueorfalse, depending on whether or not a hold could be placed - Test it using the test
Cancelling a hold
- Add a method called
cancelHoldwhich will cancel the hold on a book - Run the test
Renewing the book
-
Finally, it should be possible to renew the book if it hasn't already reached (or exceeded) the maximum number of renewals and it is not being held. Add a method called
renewwhich will attempt to renew a book and returntrueorfalseif it can be renewed. Don't forget to increment the value of thecurrentRenewalsfield. The code you you will need to increment the date is as follows:dueDate = LocalDate.now().plusDays(loanLength); - Run the test
Checking if the book is overdue
- It should also be possible to verify if a book is overDue. Create a method called isOverDue which will return
trueif the book is overdue,falseotherwise. Be careful as it should also returnfalseif the book is not on loan (and not throw an exception). A book is considered overdue if it is on loan and the due date is in the past - Run the test
The End
Your LibraryBook class should now be complete. Choosing 'LibraryBookTest' from the test dropdown will run all the tests. Verify that they all pass.