Page 1 of 9 Q1. [8 marks] Intra-Method Control Flow:

Draw a flowchart for the following method:

public void countLengths(List data) { for (String next : data) { if (next != null) { if (next.length() > CUT_OFF) { large = large + next.length(); } else { small = small + next.length(); } } } }

start

hasNext String? F

T

next != null F

T

next.length() F > CUT_OFF? T

s = s + next.length() l = l + next.length()

stop

Page 2 of 9

Note: Question 2 refers to the JetUML project checked out of the repository. Be careful not to make any changes to this code!

Q2. [12 marks] Sequence :

Draw a sequence diagram to represent a call to the getCallees() method of the CallNode class in the graph of the JetUML system. Include only the first level of calls to the Java library. Do not include arguments to methods. Assume that any for-each loop that you encounter iterates over a collection of size 2.

CN: CallNode AL: ArrayList G: Graph E: Edge

cn:CN callees:AL pGraph:G e1:E e2:E

getCallees

new ArrayList

getEdges

getStart

getEnd

add

getStart

getEnd

add

Page 3 of 9 Q3. [6 marks] The Liskov Substitution Principle

Consider the following specification for a method that belongs to a class named ColourChooser:

// REQUIRES: c is one of: "red", "green", "blue", "black", "white" // MODIFIES: this // EFFECTS: colour is set to c public void setColour(String c) { // implementation not important }

Now suppose we override this method in a subclass BWColourChooser and give it the following specification:

// REQUIRES: c is one of: "black", "white" // MODIFIES: this // EFFECTS: colour is set to c @Override public void setColour(String c) { // implementation not important }

Will an instance of BWColourChooser be substitutable for an instance of ColourChooser? In other words, can you replace an instance of ColourChooser with an instance of BWColourChooser? You must provide an answer in the context of the Liskov Substitution Principle. Answers of the form "yes, because the postcondition has been strengthened" are not sufficient – you must state why the postcondition has been strengthened.

No. You cannot replace an instance of ColourChooser with an instance of BWColourChooser. The Liskov Substitution Principle (LSP) requires that when overriding a method in a subclass, the precondition is weakened (or remains the same) and the postcondition is strengthened (or remains the same).

In the case of setColour, the precondition is strengthened in the subclass. It is strengthened because it specifies a more restrictive set of values then the precondition in the superclass. The precondition in the superclass allows the colour to be any one of "red", "green", "blue", "black", "white" whereas as the corresponding precondition in the subclass restricts the colour to be only one of "black" or "white".

Page 4 of 9

Note: Question 4 refers to the JetUML project checked out of the repository. You must not modify the code in this system in any way while answering this question. In particular, you must not add any code whatsoever.

Q4. [8 marks] Type Hierarchies

Below you will find a type hierarchy that includes some of the types in the graph package of the JetUML system. Use it to help you answer the questions on the following page.

«interface» Edge

«abstract» AbstractEdge

«abstract» ShapeEdge

«abstract» NoteEdge StateTransitionEdge SegmentedLineEdge

ReturnEdge CallEdge ClassRelationshipEdge

Page 5 of 9 For each group of statements marked (i) through (iv) below, put a check-mark ✔ alongside if every statement in the group will compile, otherwise put a cross ✗. In the case where a group of statements does not compile, you must explain why – it is sufficient to provide only one valid reason for each group of statements. No marks will be awarded for the cross ✗ alone. Note that these statements involve the types shown in the type hierarchy on the previous page.

Remember: you must not add any code to the system. In particular, you must not type any of the code below into IntelliJ.

(i) SegmentedLineEdge edge = new SegmentedLineEdge(); edge.setStartLabel("*"); ✗

SegmentedLineEdge is an abstract class and therefore cannot be instantiated.

(ii) SegmentedLineEdge slEdge = new CallEdge(); slEdge.setSignal(true); ✗

We cannot call setSignal on an object whose apparent type is SegmentedLineEdge as this method is not specified for that type.

(iii) Edge e = new ReturnEdge(); Node start = e.getStart(); ✔

(iv) CallEdge cEdge = new ReturnEdge(); ArrayList pts = cEdge.getPoints(); ✗

ReturnEdge is not a subclass of CallEdge, so we cannot assign an instance of ReturnEdge to a variable declared to be of type CallEdge.

Page 6 of 9

Note: Question 5 refers to the ShoppingList project checked out of the repository.

Q5. [18 marks] Data Abstraction: Implementation

Complete the implementation of the ShoppingList class by providing an implementation for the constructor and the four methods specified in this class. Note that a field has been provided for you – use it! You should not have to add any other fields.

You do not need to make any changes to the ShoppingItem class but you should take time to familiarize yourself with its specification.

Note that tests have been provided for you – do not modify them! If you implement the class according to its specification, the given tests will pass. Feel free to develop your code using IntelliJ but you must copy your answer onto this exam paper before the end of the exam. Note: • headers for the constructor and methods have been provided for you – you just need to fill in the body • it is not necessary to copy the comment statements • note that we have provided more space than you are likely to need.

public ShoppingList() { items = new LinkedList(); }

public void addItem(ShoppingItem item) { items.add(item); }

public boolean hasItem(ShoppingItem item) { return items.contains(item); }

Page 7 of 9 public int getTotalCostOfItems() { int cost = 0;

for(ShoppingItem next : items) { cost += next.getCost(); }

return cost; }

public int countNumberOfItemsWithCostInRange(int low, int high) { int count = 0;

for (ShoppingItem next : items) { if (next.getCost() > low && next.getCost() < high) { count++; } }

return count; }

Page 8 of 9

Note: Question 6 refers to the Roadstar project checked out of the repository.

Q6. [12 marks] Testing Robust Classes

Carefully read the specification of the methods in the Car class. In this question you will be asked to design tests for the drive method. Note that a CarTest class has been provided for you. Use this class to design your tests and be sure to copy your answer onto this exam paper before the end of the exam. You must assume that the runBefore method runs before each of your tests. Do not add fields to the CarTest class and do not modify the runBefore method. If you need to declare additional variables, you must declare them locally in your test method. a) Design a jUnit test method that tests the implementation of the drive method when the car is driven a distance that is less than Car.SERVICE_DISTANCE

@Test public void testDriveRegular() throws ServiceException { testCar.drive(Car.SERVICE_DISTANCE / 2); assertEquals(Car.SERVICE_DISTANCE / 2, testCar.getDistanceSinceLastService()); assertFalse(testCar.needsService()); }

b) Design a jUnit test method that tests the implementation of the drive method when the method is called twice on the same car object and the total distance travelled is less than Car.SERVICE_DISTANCE

@Test public void testAccumulateDistance() throws ServiceException { testCar.drive(Car.SERVICE_DISTANCE / 4); testCar.drive(Car.SERVICE_DISTANCE / 4); assertEquals(Car.SERVICE_DISTANCE / 2, testCar.getDistanceSinceLastService()); assertFalse(testCar.needsService()); }

Page 9 of 9 c) Design a jUnit test method that tests the implementation of the drive method when the car is driven a distance that is at least Car.SERVICE_DISTANCE with a single call to the drive method.

@Test public void testServiceDistance() throws ServiceException { testCar.drive(Car.SERVICE_DISTANCE); assertEquals(Car.SERVICE_DISTANCE, testCar.getDistanceSinceLastService()); assertTrue(testCar.needsService()); }

) Design a jUnit test method that tests the implementation of the drive method when a call to the method is expected to produce a ServiceException.

@Test (expected = ServiceException.class) public void testDriveNeedsService() throws ServiceException { testCar.drive(Car.SERVICE_DISTANCE); testCar.drive(1); }