CTEC2905 Object Oriented Design Exercise 7: Interfaces
Hello, dear friend, you can consult us at any time if you have any questions, add WeChat: daixieit
CTEC2905 Object Oriented Design
Exercise 7: Interfaces
Lab Objectives
• Create your own custom Interface types
• Use existing common Interface types:
o Iterable - so objects of your aggregate collection class can be used with a for-each loop, forEach method or Iterator
o Comparable - so objects of your class can be compared and sorted into order
Associated Reading:
Java Oracle Tutorials,
https://docs.oracle.com/javase/tutorial/java/IandI/createinterface.html
Interfaces
Interfaces provide a powerful way of deploying abstraction and forming a contract such that any classes implementing that interface can be guaranteed to support common behaviour, without having to share any implementation.
An Interface is very simple; it usually solely contains method declarations, without any implementation, i.e. method headers. As of Java 8 interfaces can contain static and default methods - you will see examples ofthis in the Comparator interface in a later lab exercise.
During the first part of this lab exercise you will explore how to define your own interface types and make use of them to create reusable capabilities. You will then go on to make use of interfaces provided by the Java API that abstract iteration over collections and comparison of objects. Namely: Iterable, Iterator and Comparable.
You define an interface in a similar way to a class, but use the interface keyword instead of class. When you wish to implement an interface, you place the implements reserved word and the interface type you wish to implement in the class header. You then need to provide the implementation of the method(s) declared in the interface, i.e. the method body.
Notes - The Realization association:
• Realization (Implementing an interface) “can-do”
• Illustrated in UML class diagrams using the following association symbol:
In these exercises you will also see an illustration of polymorphism. If several different classes all implement the same interface, they can be processed in a common way, as we can be certain that they will each have their own implementation of the method(s) declared in that interface.
Although an interface cannot be instantiated directly it can be used as a type in a variable declaration. Used in this way, it means that the variable be instantiated as an object of any class that implements that interface.
As you work through the exercises, make mental notes whether a type is an interface type or a class type, and identify the (class) types ofthe objects that invoke the interface methods.
Question 7.1 Reopen your Eclipse workspace called "CTEC2905_OO_Design" then
download the existing Java Project that can be extracted from the "Interfaces.zip" file, and imported into Eclipse. Recall the previous classes you have worked with related to playing dice: Die, PairOfDice and MultipleDice. You may have noticed that each of these implementations share two common methods: getScore() and roll(). We have abstracted this common functionality into an interface Rollable, forming a simple API for all types of dice implementations to adhere to.
In the main package, take a look at the PolymorphismDemo program, and how there is a list of type Rollable populated with a variety of different rollable types. The for-each loop processes each of these in a uniform way, but will use dynamic method lookup to invoke the correct roll and getScore method depending on the specific class type of each object. This is an example of polymorphism.
It's also possible to invoke toString in this way, as every class automatically inherits toString, so no matter the type, the compiler can be satisfied the object will have a toString implementation.
Question 7.2 [Portfolio A.2] Download the existing Java Project that can
be extracted from the "PortfolioPlayer.zip" file, and imported into Eclipse. This question forms the second part of Portfolio case study A, and you will need to have already attempted Portfolio A.1 from the previous exercise. Copy your Player class from the previous exercise into the src > lib package of the PortfolioPlayer project.
Study the UML diagram below - you should see the Rollable hierarchy and a familiar Player class, that you started working on in the previous exercise.
In the test > lib package you have been given a JUnit Test Case PlayerTest containing unit tests, which will be used to mark your implementation of the Player class. You can use it to gain progressive feedback on your design, however, some tests will not pass at this stage and are designed to test functionality that you will add later in this exercise. It is important that you use the same method signatures and return types as those specified in the test program, e.g. note the method called “setFullPlayerName” invoked in the testSetFullPlayerName unit test meaning this exact name needs to be used for the corresponding method in your Player class.
a) Update the Player class so that rather than holding a field of type PairOfDice, it instead holds a field of type Rollable. You will then need to update the three-argument custom constructor so it accepts any Rollable type, however the other constructors can assign a default object of your choice to this variable, so long that it is rollable.
b) The getPairOfDice method should be updated to getRollable, with a return type of Rollable.
c) You should run the unit tests in PlayerTest and check the progress you have made for this question and the general design ofthe class you started in the previous exercise.
d) Add a further void method setFullPlayerName(...) that accepts a single String argument (e.g. "Joe Bloggs") and then uses this to set the first and family name individually by extracting the relevant information and then calling the respective setter methods of the Name class. The first and family name should be capitalised (e.g. to "Joe" and "Bloggs") regardless of the case of the string argument – so both "JOE BLOGGS" and "joe bloggs" should generate the same result.
e) Add a further void method generateGamerTag(...) that accepts an int argument and then uses this to generate a gamertag. The method should firstly ensure the number provided is between 1 and 100 – anything outside of this range should result in no change. If the number provided is legitimate, the gamertag should be updated to be the player’s full name in reverse order (in lowercase and with no whitespace) with the given number appended. For example, if the number 50 was provided and the player’s name was "Joe Bloggs", the gamertag should be set to "sggolbeoj50".
f) In the src > main package, you will notice a class called PlayerApp that has a method called execute, which accepts an ArrayList
• Return a String that contains the name of each player (which meets the criteria noted below *) in the ArrayList
• * only players whose gamertag contains their surname (regardless of case) as well as the int number passed as a parameter to the execute method.
In the test > main package there is a further JUnit Test Case PlayerAppTest containing a unit test called testExecute, which assesses whether the execute method behaves correctly.
Please note: Your solution to part f) will be marked by using a different input data set of players within the list to ensure your solution works dynamically based upon any given data set and is not hardcoded in any way.
g) As this is a portfolio question, you should update the Javadoc you have previously written for the Player class where necessary to ensure it accurately reflects the updated
class.
Interfaces for iteration and comparison
When we create a class that encapsulates a collection of objects we would normally expect to have the ability (note: 'can-do' behaviour) to access each element in turn (i.e. iteration). For certain types of object it might also make sense to sort them in a particular order. Sorting objects requires the ability to compare them in pairs to establish which one is logically before the other (i.e. comparison). These behaviours are so common in programming, that the Java API provides common interfaces to support them. Java programmers should make use of these interfaces so that their own classes conform to standard practices. They exist in the following packages:
java.lang: Comparable, Iterable
java.util: Iterator
Question 7.3 Revisit the Interfaces project. For this question you will work on the
PlayList and Song classes inside the lib.iterable_comparable package and the PlayListApp program in the main package.
a) Let’s study the Iterable interface: in the main method of PlayListApp, attempt to write a for-each loop that iterates through each Song object within a PlayList instance, e.g.
for (Song track : playlist) {
track.play();
}
b) You should receive an error that states: "Can only iterate over an array or an instance of java.lang.Iterable". In other words, the for-each loop only works on arrays or classes that implement the Iterable interface. Look at the Java API for the Iterable interface: https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/lang/Iterable.html and study its description and the iterator and forEach methods it defines. Note that ArrayList
is one of the "All Known Implementing Classes". This is why you have been able to use a for-each loop to iterate through elements within an ArrayList instance before.
c) Additionally, replace the body of the PlayList’s mergePlaylist(...) method as follows: p.forEach(s -> songlist.add(s));
Currently, the forEach method is not available to your PlayList type.
d) If you want to be able to use a for-each loop or forEach method for your own collection classes, then you need to ensure they implement the Iterable interface. In your PlayList class firstly change the class header to be:
public class PlayList implements Iterable
e) Then add the following method, noting how the method header matches that specified within the API, with the type generic parameter T replaced with Song in this case:
public Iterator
return songlist.iterator();
}
f) Conveniently, as ArrayList also specifies an iterator() method you can simply delegate to that. You will also need to import: import java.util.Iterator;.
g) Your for-each loop in the PlayListApp program from part a) and the forEach method from part c) should now compile and work as expected. Test them to ensure they do.
Question 7.4 Continue working in the same project, and look inside the
lib.polymorphism package at the MultipleDice class.
a) Firstly, ensure the aggregation class MultipleDice implements Iterable and defines the iterator() method (as shown in the previous question for the PlayList class). Note: Your MultipleDice class already implements the Rollable interface, however, you can legitimately implement multiple interfaces, simply separating them with a comma, e.g. implements Rollable, Iterable
b) In the main package, create a demo program called IterableDemo, and within the main method create a new instance of MultipleDice, then using a for-each loop, iterate through each element, printing the output of the toString method. Then try the same below this but by using the forEach method.
Question 7.5 [Portfolio B.2] This question forms the second part of
Portfolio case study B, and you will need to have already attempted Portfolio B.1 from the previous exercise. Revisit the PortfolioRegister project, and study the UML diagram below.
a) Update the Register class so that it implements the Iterable interface and defines an appropriate iterator method.
b) Run the RegisterTest Test Case in the test > lib package to ascertain if the testIterator() unit test now passes
c) As this is a portfolio question, you should update the Javadoc in the Register class for the newly added method.
Notes - Using the Iterator: • You should also be aware that you can use the Iterator that is returned by your iterator() method to process each of the elements in your collection. Go to the Java API for the Iterator interface: https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/util/Iterator.html and study its description and the two non-default methods next and hasNext. • When you use a for-each loop the following is generated behind the scenes:
Iterator while (itr.hasNext()) { itr.next().play(); } |
Question 7.6 Look up the documentation for the java.lang.Comparable interface in the
Java APIand then attempt the following tutorial questions:
a) If x.compareTo(y) > 0 is true, what would you expect y.compareTo(x) to be?
b) If x.compareTo(y) == 0 is true, what would you expect x.equals(y) to be?
c) What is the expected value of x.compareTo(x)?
d) If x.compareTo(y) < 0 is true, and if y.compareTo(z) < 0 is true, what can you say about x.compareTo(z)?
Question 7.7 The String class implements the Comparable interface. In the main
package of your current project create a new Java class called CompareStringDemo.
a) Look at the compareTo method description for the String class in the Java API: https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/lang/String.html you will see that the return result is either negative, zero or positive.
b) Copy the following code into the main method of your CompareStringDemo program:
String s1 = "Jim";
String s2 = "Fred";
if (s1.compareTo(s2) < 0 ) { // then s1 is before s2 System.out.println(s1 + " is before " + s2);
}
else if (s1.compareTo(s2) == 0 ) { // then s1 and s2 same System.out.println(s1 + " is the same as " + s2);
}
else if (s1.compareTo(s2) > 0 ) { // then s1 is after s2 System.out.println(s1 + " is after " + s2);
}
c) Experiment with different values for s1 and s2, including lower and upper case characters. Can you see how strings are compared based on the Unicode value of each character in the strings?
Question 7.8 Continue working in the same project and revisit the
lib.iterable_comparable package containing the classes PlayList, Song and the main package that contains PlayListApp:
a) In the PlayList class, add a new method, that attempts to sort the list into an order:
public void sortPlaylist() {
Collections.sort(songlist);
}
b) You should receive an error - you may not understand what it is complaining about, but in simple terms the sort(...) method belonging to the Collections class requires the class type that is stored within the collection (passed as a parameter) to implement the Comparable interface. In this case, songlist represents an ArrayList
c) Go to the Song class and firstly change the class header to the following: public class Song implements Comparable
d) You then need to define the compareTo(...) method, which this interface requires. There is no set rule on exactly how you want to order objects of your own custom classes - it's up to you. Let's firstly focus on the song title; add the following method to your Song class:
public int compareTo(Song other) {
return this.title.compareTo(other.title);
}
e) As the title field is of type String and the String class defines a compareTo(...) method itself, you can delegate to that. Now go back to your PlayList class, you should notice that the sortPlaylist() method you wrote in part a) is not showing errors anymore. Go to the PlayListApp program and add the following code:
playlist.sortPlaylist();
System.out.println(playlist.getTrackListings());
f) If you run the program, you should now see that the tracks have been ordered on their title. What if you had two songs with the same title, but a different artist and duration? Manually edit one of the Song objects added to the PlayList towards the top of the program to ensure it has the same title as another song, but a different duration and artist.
g) Let's now ensure the compareTo(...) method in the Song class compares title, followed by artist, followed by duration:
public int compareTo(Song other) {
int result = this.title.compareTo(other.title);
if (result == 0) {
result = this.artist.compareTo(other.artist);
if (result == 0) {
result = Integer.compare(this.duration, other.duration); }
}
return result;
}
h) This updated version will firstly compare titles, however, if this results in 0, i.e. they have the exact same title, it compares artists and upon artists being identical it compares duration. Note theInteger.compare(int, int)utility method
i) Go back to your PlayListApp program and ensure your compareTo(...) method works correctly by sorting songs with different details and some similarities.
j) Continuing in the same program, create two Song objects and then use the compareTo(...) method to see whether a positive, negative or zero integer is returned.
2021-12-29