Software Design Sample midterm solutions 1. The administration at Happy Valley School District is redesigning the software that manages information about its students. It has identified an abstract class Student, with two subclasses: ElementaryStudent and MiddleSchoolStudent. MiddleSchoolStudent has a subclass called HighSchoolStudent. The basic organization of these classes is shown below. public abstract class Student { private String name; public class ElementaryStudent extends Student { // This array would have one entry for each year of elementary school // identifying the student s primary teacher private Teacher[] teachers; public class MiddleSchoolStudent extends Student { // All students take the 5 subjects shown below. This remembers who // taught each of those subjects in each year. private Teacher[] mathteacher; private Teacher[] englishteacher; private Teacher[] scienceteacher; private Teacher[] historyteacher; private Teacher[] languageteacher; public class HighSchoolStudent extends MiddleSchoolStudent { // In addition to the subjects in middle school, high school students // can take electives. We need to remember those teachers, too. private Teacher[] elective1teacher; private Teacher[] elective2teacher; private Teacher[] elective3teacher; Suggest another way that this information could be represented and explain what the advantages of your solution are. There is obviously a lot more information that you would want in such a system, but you should not concern yourself with that. Your redesign should keep the same basic information: which teachers did a student have, for which subjects and in which years. In particular, you do not need to model courses, grades, semesters, etc. The use of inheritance in this way is wrong. First, it fails the is-a test because a high school student is not a middle school student. Second, when a student moves from one level of schooling to another, it would be necessary to create a new object to!1
represent that student and the previous history of the student would be lost. So, we will first eliminate the inheritance. Using an array that is indexed by the year of school seems like a good way to keep track of which teachers a student had in which years. It would seem good to associate teachers with subjects in a better way than through the variable names. So, we will use a two-dimensional array, where the second dimension is indexed by subject, with subject indexes defined as constants. Finally, for electives, we will just keep an array of elective teachers for each year, indexed by a counter. We end up with a design like this: public class Student { private String name; private static final int MATH = 0; private static final int ENGLISH = 1; private static final int SCIENCE = 2; private static final int HISTORY = 3; private static final int LANGUAGE = 4; private Teacher[] elementaryteachers; private Teacher[][] subjectteachers; private Teacher[][] electiveteachers;... 2. Please explain when each of the following ways of writing a method in a superclass and overriding it in a subclass are appropriate. When it says // Code here or // More code here, it means there would be actual code, not just the comment that I show. a. class Superclass { // Code here class Subclass extends Superclass { super.m(); // More code here This is the typical way of overriding a method. It performs the same as the superclass and adds some extra behavior. b. class Superclass { // Code here class Subclass extends Superclass {!2
super.m(); There is no point to this. This is overriding a method but doing exactly the same thing as the inherited method. c. class Superclass { // Code here class Subclass extends Superclass { This is a bad idea. One should never override a method to do nothing. An overriding should honor the contract of the method that it overrides, and this almost certainly does not! d. class Superclass { class Subclass extends Superclass { super.m(); // More code here This is the same situation as a. It is a typical overriding, where the superclass implementation did nothing. In general, the implementer of a subclass might not have access to the superclass code and so could not distinguish cases a and d. e. class Superclass { public abstract void m(); class Subclass extends Superclass { super.m(); // More code here This one does not compile. It is not possible to call super.m() since it is abstract. 3. The following questions concern preconditions, postconditions and inheritance. a. For the following class and its description, identify the preconditions and postconditions of the constructor and each method. public Counter() // Initializes a counter to 0 public int getvalue () // Returns the value of the counter public void increment () // Increases the value of the counter by 1 Constructor: no precondition, postcondition value is 0!3
getvalue: no precondition, postcondition returns current value increment: no precondition, postcondition value is increased by 1 b. Consider the following subclass of Counter, called Counter2. Identify the preconditions and postconditions of its constructor and methods. Does Counter2 follow the rules about how preconditions and postconditions should change when sub typing? Why or why not? public Counter2() // Initializes a counter to 0 public void increment () // Doubles the value of the counter Constructor: no precondition, postcondition value is set to 0 increment: no precondition, postcondition value is doubled Not a valid subtype because the postcondition has been changed in a way other then strengthening it c. Consider the following subclass of Counter, called Counter3. Identify the preconditions and postconditions of its constructor and methods. Does Counter3 follow the rules about how preconditions and postconditions should change when sub typing? Why or why not? public Counter3() // Initializes a counter to 0 public void increment (int n) // If n > 0, adds n to the value of the // counter Constructor: no precondition, postcondition value is set to 0 increment: precondition n > 0, postcondition value is increased by n This is a valid subtype because this increment function is an overloading, not an overriding.!4
4. Take a look at the Java API for Stack available online at http://docs.oracle.com/javase/ 8/docs/api/. a. What problems do you see with where Stack is placed in Java s type hierarchy? Stack extends Vector. That means that there are a lot of non-stack methods that can be applied to and would cause the stack to stop behaving like a Stack. Examples are: add (int index, E element) elementat (index) insertelementat (E obj, int index) remove (int index) remove (Object o) b. Suggest an alternative design that avoids these problems. A better alternative would be to use composition rather than inheritance. In this way Stack would not extend any other classes. It would contain an instance variable whose type is Vector. Any methods that it would have been reasonable to inherit from Vector now become delegation methods. Examples are: capacity() clear() clone() ensurecapacity() isempty() size() trimtosize() c. Discuss your alternative design. What are the advantages and disadvantages of your proposal compared to what Java provides? The modified design has the big advantage that a Stack will always behave like a Stack. With the original design, a Stack can be used anywhere that a Collection is expected. The program could then manipulate the stack in non-stack-like ways, which would likely cause errors. A disadvantage of the modified design is that stacks could be passed as parameters to fewer methods. The more restrictive typing might prevent passing the stack to methods that only operated on it in stack-like ways, but using operations in the Collection interface. For example, if a method only modified a collection by adding and removing at the end, it would be treating the collection as a stack. These calls would no longer be valid. There is also a small disadvantage in the need to write the delegation methods. However, these are very simple to write and should not be a deterrent. Overall, the second design is better because it prevents non-stack-like behavior on the stack. It may be necessary to change some other code or duplicate code in cases where the existing code passes stack as collections but still treats them as stacks.!5
5. A Date class consists of the following: public class Date { private int month; private int day; private int year; public Date (int month, int day, int year) { this.month = month; this.day = day; this.year = year; public void adddays (int numdays) { public boolean isleapyear () { a. Describe in English the class invariants for this class. month must be between 1 and 12 day must be between 1 and 31 for months 1, 3, 5, 7, 8, 10, 12 day must be between 1 and 30 for months 4, 6, 9, 11 day must be between 1 and 28 for month 2 when isleapyear returns false day must be between 1 and 29 for month 2 when isleapyear returns true b. Write a wellformed method to check the class invariants. public boolean wellformed () { switch (month) { case 1: case 3: case 5: case 7: case 8: case 10: case 12: return day >= 1 && day <= 31; case 4: case 6: case 9: case 11: return day >= 1 && day <= 30; case 2: if (isleapyear()) return day >= 1 && day <= 29; else return day >= 1 && day <= 28; default: return false;!6
c. What are the preconditions and postconditions for the constructor, adddays and isleapyear methods? The constructor s preconditions are the same as the class variant but applied to the parameters. The postcondition is that the Date is well formed. For adddays, there is no precondition. The postcondition is that the Date is modified to be the number of days into the future if the parameter is positive, and the number of days in the past if the parameter is negative. It will wrap around the ends of months and years correctly. For isleapyear, there is no precondition. Its postcondition is that it will return true if the year inside the date is a leap year. It is a leap year if the year is divisible by 4. There are other rules, too, which students might or might not know. (If divisible by 100, it is not a leap year. If divisible by 400, it is a leap year.) d. If you wanted Date objects to be immutable, what would you change about the design above and your answers to the previous parts of this question? I would change the class and instance variables to be final. adddays would return a Date rather than be void. It would return a new date instead of modifying the existing date. e. What are the advantages or disadvantages to having immutable Date objects? With immutable Date objects, there is no need to worry about surprising side effects as a Date can never change. This means that we can treat Dates like values and easily have multiple events on a calendar for example, reference the same Date object without needing to be concerned that moving one event to a different date by calling adddays would affect any other events scheduled for that day. The disadvantage is that whenever we call adddate, we will need to create a new Date object. Overall, it would be preferable for Dates to be immutable. 6. Each of the following has a design flaw. Discuss what the flaw is and suggest a way to remove the flaw. a. public class KeypadLock { private int[] secretcode; public KeyPadLock(int[] code) { secretcode = code; public boolean checkcode (int[] code) { return Arrays.equals(secretCode, code); The array passed to the constructor should be copied into the instance variable. If that is not done, then the part of the program that created the KeypadLock could modify the key or pass the original key around to other parts of the program that could modify the key. That would be an undesirable side effect.!7
b. Employee -name -address HourlyEmployee - hourlyrate SalariedEmployee - annualsalary Retired The problem with this hierarchy is that it is trying to capture information with subclasses that is a potentially changing role. For example, an hourly employee might become a salaried employee, or an employee might retire. With the current arrangement, when that type of transition occurs, we would need to create a new object, move the relevant data to the new object, and update all references to the old object to point to the new object. A better solution would be to use composition: Employee -name -address «abstract» EmploymentStatus HourlyEmployee - hourlyrate SalariedEmployee - annualsalary Retired c. public class DailySales { private ArrayList<Sale> sales; // Assume sales is appropriately set. public int totalsales () { int total = 0; for (Sale s : sales) { Payment payment = s.getpayment(); total = total + payment.getamount(); return total; This is a violation of the Law of Demeter, as DailySales should not send methods to an object returned by the getpayment method. A better solution would be to have a method in the Sale class called getamount, that returned the amount directly by asking the Payment object for the amount. Then we would have: public int totalsales () { int total = 0; for (Sale s : sales) {!8
total = total + s.getamount(); return total; getamount would look something like this: public int getamount () { return payment.getamount(); 7. For each of the examples below indicate if this is a good use of inheritance or not. If it is not, briefly explain why not and what would be a better way to model the relationship between the classes. a. (3 points) An airplane has a model (like Boeing 747) and belongs to an airline. A flight has a starting and destination airport and a flight number and requires the use of an airplane. Should Flight be modeled as a subclass of Airplane? No, it fails the is-a test. Flight is-not-an airplane. Composition should be used instead. Flight should have an instance variable that is an airplane. b. (3 points) An academic department has many types of employees including teaching assistants and faculty, with one designated faculty member who is the chair of the department. Should Chair be a subtype of Faculty? No, chair is a changing role. Faculty should have a list of roles, of which being a chair is one. And/or Department should have a chair instance variable that refers to the Faculty member currently playing the role of the chair. c. (3 points) A textbook is a book that has questions at the end of each chapter. Should Textbook be a subtype of Book? Yes, this is a good use of inheritance. d. (3 points) A list is an ordered collection of values. An immutable list is a list whose values cannot change after the list is created. Should ImmutableList be a subclass of List? No, ImmutableList and List do not have a shared interface. List s interface would include things like an add method, which would be inappropriate for ImmutableList. It would be better for ImmutableList to have an instance variable whose type is List so it can control the API. e. (3 points) Snickers is the name of my dog. Should Snickers be a subclass of Dog? No, Snickers is not a class. It is an instance of Dog.!9
8. Answer the questions below about this UML class diagram. «abstract» BankAccount int balance deposit (int amount) withdraw (int amount) + Customer String name String address openaccount () closeaccount (Account act) CheckingAccount int insufficientfundsfee processcheck (Check c) SavingsAccount double interestrate depositinterest() a. (1 point) What are the names of the methods that can be called on any BankAccount? deposit, withdraw b. (1 point) What are the names of all the methods that can be called on a CheckingAccount? deposit, withdraw, processcheck c. (1 point) How many Customers can be associated with a BankAccount? Exactly 1 d. (1 point) How many BankAccounts can a Customer have? 1 or more e. (1 point) Would the following statement be legal, assuming the constructor exists and the variable cust has type Customer? BankAccount acct = new CheckingAccount (cust); Yes.!10
f. (5 points) How would you change the design so that a married couple could share a BankAccount? Draw the revised UML diagram. «abstract» BankAccount int balance deposit (int amount) withdraw (int amount) + AccountHolder openaccount () closeaccount (Account act) CheckingAccount int insufficientfundsfee processcheck (Check c) SavingsAccount double interestrate depositinterest() Customer String name String address 2 MarriedCouple The figure above is one possible solution.!11
9. In Java s Collection interface, the add method is described as follows: boolean add(e e) Ensures that this collection contains the specified element. Returns true if this collection changed as a result of the call. Parameters: e - element whose presence in this collection is to be ensured Returns: true if this collection changed as a result of the call a. (2 points) What are the preconditions of Collection s add method? There are no preconditions b. (2 points) What are the postconditions of Collection s add method? e will be in the collection. Returns true if e was not in the collection before add was called and returns false if it was already in the collection. The List interface extends Collection. Its add method is describe as follows: boolean add(e e) Appends the specified element to the end of this list. Parameters: e - element to be appended to this list Returns: true c. (2 points) What are the preconditions of List s add method? There are no preconditions. d. (2 points) What are the postconditions of List s add method? e is added to the end of the list and true is returned. e. (2 points) Does List strengthen, weaken or not change Collection s precondition for add? Not changed f. (2 points) Does List strengthen, weaken or not change Collection s postcondition for add? Strengthen. It indicates where the new element will be and that it always returns true.!12
10.I would like you to reason about how Liskov s Substitution Principle helps us understand the subtyping rules associated with generics. The ArrayList class implements the List interface. For this question, focus on the behavior of the get and set methods in the List interface: Interface List<E> E get(int index) Returns the element at the specified position in this list. E set(int index, E element) Replaces the element at the specified position in this list with the specified element a. (2 points) Assuming that Java allows ArrayList<String> to be treated as a subtype of List<String>, does using List and ArrayList in the following way satisfy Liskov s Substitution Principle? Briefly explain your answer. List<String> mylist = new ArrayList<String>(); Yes, this is fine. The signatures of the inherited methods in the ArrayList would all still take Strings as parameters and return Strings as results, so any method defined for List<String> can be called if the object is actually an ArrayList<String> and the caller will not be surprised. b. (2 points) Assuming that Java allows ArrayList<String> to be treated as a subtype of ArrayList<Object>, does using ArrayList in the following way satisfy Liskov s Substitution Principle? Briefly explain your answer. ArrayList<Object> mylist = new ArrayList<String>(); This would not satisfy Liskov s substitution principle. Here the object is supposed to contain just strings. However, when manipulating it via mylist, the add method might insert an object that was not a string, which would surprise someone who thought it should only contain strings. The following code snippet makes the problem clearer. ArrayList<String> mystrings = new ArrayList<String>(); ArrayList<Object> myobjects = mystrings; myobjects.add (new Integer (3)); String lastentry = mystrings.getlast(); Here, we would expect a String result, but the last entry in the ArrayList is actually an Integer. In fact, Java does not allow generics to be subtyped this way. The type of the element cannot change, while the type of the collection can change.!13