Table of Contents
You have been asked to code a Windows application to manage the users of the UC3M swimming pool.
The application should be a very simple database system, which would store the data of all the swimming pool users. It must be capable of listing all the users and their data.
There are 5 types of swimming pool users:
Outsider: people not related to the UC3M, e.g. people living in Leganés. The only datum we know from them is their DNI (the Spanish national identity card).
Staff: Office workers and support people on the campus, but not teachers. We know their DNI and their salary.
Professor: UC3M teachers. We know their DNI, their department and their salary.
Student: We know their DNI and their NIA (Student identification number).
Intern: Regular students that are also working for the university. We know all their student data and also their salary.
Which of the following diagrams best represent the class hierarchy of the application? Discuss the pros and cons of all diagrams with a classmate for 15 minutes.
There is no right and wrong answer to this exercise, but it raises some good points.
Diagram A is too simple, as it does not make use of a class
hierarchy: there are a lot of code redundancies, for instance,
the handling of some class attributes like dni
and
salary
must be done in many classes.
On top of that, some of the dependencies between classes in the
exercise are not properly represented in the diagram, for
example, Intern
s should also be
Student
s.
Diagram B is better, as it makes uses of class hierarchies, but it has some problems:
People form Staff
cannot be
Professor
s, as the exercise explains.
There is still too much code redundancy, for instance the
dni
and salary
attributes are
present in too many classes.
In diagram C, the class Outsider
has been
substituted by Person
, a much more generic
class. This allows eliminating the code redundancies for the
handling of the dni
attribute. The downside of this
approach is that outsiders have now to be instances of the
Person
class; therefore some of the semantics of
the exercise are lost. This can lead to problems in the future,
if we want to modify the application or add more types of users.
And there is still the problem of Professor
s that
are also Staff
, which is not allowed by the
exercise, and the code redundancy in the salary
attribute.
Diagram D is very nice, there is no code redundancy on the
dni
attribute and Professor
s are no
longer part of the Staff
. There are still some code
redundancies for the salary
attribute, though.
In Diagram E, the problem of the code redundancy on the
salary
attribute is fixed by using the new
superclass Worker
. Sadly, Java does not allow
inheriting from more than one class at the same time, thus this
approach is impossible to implement. In future lessons, you will
learn about Java Interfaces, a nice way to
implement multiple inheritance in Java. But, for now, this
approach is not possible.
Long story short: Diagram D is the preferred solution, even if we have to cope with some code redundancy for the salary, because, at the time being, we cannot do better.
Implement the application using diagram D as a reference. Code all
the classes. Each class must declare all its attributes, a
constructor and also a method String toString()
to
generate a textual representation of the known data of each user,
according to the format below.
You must also code a test class, with only the main
method. It should instantiate objects representing each user
(listed below) to test the application. You must store all the
user objects in a variable that can hold several
Person
objects (any Java collection will do, e.g. an ArrayList<Person>
...). Then
go through all the users in the collection, printing all their
data to the standard output.
The expected behavior of your application must be like this (the order in which the users are printed is not relevant):
C:\Users\Alberto>java DataBase DNI: 01100000-A DNI: 00220000-B DNI: 00030000-C, salary: 2000 DNI: 04040000-D, salary: 1500 DNI: 50500000-E, salary: 1000, department: mathematics DNI: 66600000-F, salary: 2000, department: telematics DNI: 77000000-G, NIA: 777777 DNI: 88080000-H, NIA: 888888 DNI: 90990000-I, NIA: 999999, salary: 400 DNI: 10100000-J, NIA: 101010, salary: 800
#### Person.java ##################### public class Person { private String dni; public Person(String dni) { this.dni = dni; } public String toString() { return "DNI: " + dni; } } #### Staff.java ###################### public class Staff extends Person { private int salary; public Staff(String dni, int salary) { super(dni); this.salary = salary; } public String toString() { return super.toString() + ", salary: " + salary; } } #### Professor.java #################### public class Professor extends Staff { private String department; public Professor(String dni, int salary, String department) { super(dni, salary); this.department = department; } public String toString() { return super.toString() + ", department: " + department; } } #### Student.java #################### public class Student extends Person { private String nia; public Student(String dni, String nia) { super(dni); this.nia = nia; } public String toString() { return super.toString() + ", NIA: " + nia; } } #### Intern.java ##################### public class Intern extends Student { private int salary; public Intern(String dni, String nia, int salary) { super(dni, nia); this.salary = salary; } public String toString() { return super.toString() + ", salary: " + salary; } } #### DataBase.java ################### import java.util.ArrayList; public class DataBase { public static void main(String args[]) { // All users are stored in the same collection, any Java // collection will do for this simple exercise ArrayList<Person> users = new ArrayList<Person>(); // Fill the database with all users' data { Person p = new Person("01100000-A"); users.add(p); p = new Person("00220000-B"); users.add(p); Staff s = new Staff("00030000-C", 2000); users.add(s); s = new Staff("04040000-D", 1500); users.add(s); Professor f = new Professor("50500000-E", 1000, "mathematics"); users.add(f); f = new Professor("66600000-F", 2000, "telematics"); users.add(f); Student st = new Student("77000000-G", "777777"); users.add(st); st = new Student("88080000-H", "888888"); users.add(st); Intern in = new Intern("90990000-I", "999999", 400); users.add(in); in = new Intern("10100000-J", "101010", 800); users.add(in); } // Print all users for (int i=0; i<users.size(); i++) { Person p = users.get(i); System.out.println(p); } } }
Discuss with a classmate how is it possible to print all user
data (data from Staff
,
Student
... objects) if all of them are handled
through references to a simple Person
object.
Discuss with a classmate why do you need to declare the
String toString()
as a public method on
Person
and all its subclasses. Would it be
possible to declare that method package-private instead, for
example?
In Java, overridden method resolution is performed by virtual method invocation. This means that Java will resolve a method by looking at the class of the actual object and ignoring the class of the reference being used to handle the object.
In Java, a method signature includes the access modifiers, therefore, when you want to override a method, you must keep its original access modifiers.
Just before submitting your application, the UC3M asks you to implement 2 extra functionalities:
Add a new user type Tenured. They are
like standard Professor
s but have a fixed salary
of 2500 Euros. This means that the class constructor should
not have a salary argument. Also add these two instances of
tenured professors to the database:
DNI: 11110000-K, salary: 2500, department: geography DNI: 12120000-L, salary: 2500, department: mathematics
The application must support a command line argument
"-s
" for generating "short" listings, this is,
only the basic data from the users must be printed (only the
data available from the class Person
).
The expected behavior of your application must be like this:
; java DataBase DNI: 01100000-A DNI: 00220000-B DNI: 00030000-C, salary: 2000 DNI: 04040000-D, salary: 1500 DNI: 50500000-E, salary: 1000, department: mathematics DNI: 66600000-F, salary: 2000, department: telematics DNI: 77000000-G, NIA: 777777 DNI: 88080000-H, NIA: 888888 DNI: 90990000-I, NIA: 999999, salary: 400 DNI: 10100000-J, NIA: 101010, salary: 800 DNI: 11110000-K, salary: 2500, department: geography DNI: 12120000-L, salary: 2500, department: mathematics ; java DataBase -s DNI: 01100000-A DNI: 00220000-B DNI: 00030000-C DNI: 04040000-D DNI: 50500000-E DNI: 66600000-F DNI: 77000000-G DNI: 88080000-H DNI: 90990000-I DNI: 10100000-J DNI: 11110000-K DNI: 12120000-L
Estimate how long it will take you to implement each of these new functionalities, in hours or minutes.
Implement these two functionalities in a new version of your application and compare the time it took you with your original estimation.
Adding the new Tenured
class is simple: just
inherit form Professor
, define a class constant for
the fixed salary and define a constructor using that constant in
its call to the constructor of the superclass. It should take
you about 5 or 10 minutes. Please note that you should not
override the String toString()
in this class.
Detecting the command line argument is also simple. Add the
necessary logic to process the command line arguments, detect
the usage of "-s
" and also add some error checking
for invalid arguments.
The fastest way to implement the short listing mode of operation
is to add a new method to Person
to generate a
short listing of a user. As all other user classes are
subclasses of Person
they will automatically get
this new functionality. This should take you between 10 and 15
minutes.
#### Tenured.java ##################### public class Tenured extends Professor { private static final int TENURED_SALARY = 2500; public Tenured(String dni, String department) { super(dni, TENURED_SALARY, department); } } #### Person.java ##################### public class Person { private String dni; public Person(String dni) { this.dni = dni; } public String toString() { return "DNI: " + dni; } public String toStringShort() { // return toString(); -- WRONG // return this.toString(); -- WRONG // // Sadly, none of these will work, as virtual method invocation // for subclasses will use their own subclass.toString() method, // hence code redundancy is unavoilable here return "DNI: " + dni; } } #### DataBase.java ################### import java.util.ArrayList; public class DataBase { public static void main(String args[]) { // By default, listings are in long format boolean shortListingDesired = false; // Parse command args if (args.length > 1) { System.err.println("Invalid number of arguments"); return; } else if (args.length == 1) { if (args[0].equals("-s")) { shortListingDesired = true; } else { System.err.println("Invalid argument: " + args[0]); return; } } // All users are stored in the same collection, any Java // collection will do for this simple exercise ArrayList<Person> users = new ArrayList<Person>(); // Fill the database with all users' data { Person p = new Person("01100000-A"); users.add(p); p = new Person("00220000-B"); users.add(p); Staff s = new Staff("00030000-C", 2000); users.add(s); s = new Staff("04040000-D", 1500); users.add(s); Professor f = new Professor("50500000-E", 1000, "mathematics"); users.add(f); f = new Professor("66600000-F", 2000, "telematics"); users.add(f); Student st = new Student("77000000-G", "777777"); users.add(st); st = new Student("88080000-H", "888888"); users.add(st); Intern in = new Intern("90990000-I", "999999", 400); users.add(in); in = new Intern("10100000-J", "101010", 800); users.add(in); Tenured t = new Tenured("11110000-K", "geography"); users.add(t); t = new Tenured("12120000-L", "mathematics"); users.add(t); } // Print all users for (int i=0; i<users.size(); i++) { Person p = users.get(i); if (shortListingDesired) System.out.println(p.toStringShort()); else System.out.println(p); } } }