CSE 331: Software Design and Implementation, Autumn 2026
Your job is to deliver code that you know works.
ListQueue, a help queue on a list7private List<String> queue = new ArrayList<String>();7public List<String> queue = new ArrayList<String>();
8
9public int size() {
10 return queue.size();
11}
12
13public void join(String netid) {
14 if (queue.contains(netid)) {
15 throw new IllegalArgumentException(netid + " is in the queue");
16 }
17 queue.add(netid);
18}join turns away a student who is already waiting
What if line 7 said public?
4ListQueue queue = new ListQueue();
5queue.join("ada");
6queue.join("bob");
7// Skip join and change the list itself.
8queue.queue.add("ada");
9queue.queue.add(0, "eve");
10System.out.println(queue.waiting());
11System.out.println("helping " + queue.next());queue.queue: the field named queue inside ListQueue
With the field public, what does it print?
$ javac Shortcut.java $ java Shortcut [eve, ada, bob, ada] helping eve
ada is waiting twice, and eve cut in line: both rules broken
With line 7 of ListQueue.java made public
ListQueue: its join checksShortcut, which skip join$ javac Shortcut.java Shortcut.java:8: error: queue has private access in ListQueue ⋮ Shortcut.java:9: error: queue has private access in ListQueue ⋮ 2 errors
Code outside ListQueue now gets only its methods
4ListQueue queue = new ListQueue();
5// Students join at the back.
6queue.join("ada");
7queue.join("bob");
8// The TA shows who is waiting, then helps the first.
9System.out.println(queue.waiting());
10System.out.println("helping " + queue.next());What does it print?
$ javac OfficeHours.java $ java OfficeHours [ada, bob] helping ada
Only join and next changed the queue, and both rules held
ListQueue's operations, written down3/**
4 * The queue for office hours: the students waiting for help, in
5 * the order they joined. A student is named by their NetID, and a
6 * student is in the queue at most once.
7 */
8public interface HelpQueue {
9 /** @return the number of students waiting */
10 int size();
11
12 /**
13 * @modifies this
14 * @effects adds the student at the back of the queue
15 * @throws IllegalArgumentException if the student is already in
16 * the queue, and leaves the queue unchanged
17 */
18 void join(String netid);ListQueue's operations, continued20 /**
21 * @modifies this
22 * @effects removes the student at the front of the queue
23 * @return the NetID of the student removed
24 * @throws NoSuchElementException if the queue is empty
25 */
26 String next();
27
28 /** @return the students waiting, front first, in a new list */
29 List<String> waiting();
30}No fields: every specification is about the students waiting
ListQueue's line 6 says implements HelpQueue
Definition: abstract data type (ADT)
An abstract data type is a type defined by specs for its operations. The abstract state is how clients should think about the object.
HelpQueue's abstract state: the students waiting, in the order they joined
Definition: representation
The representation of an ADT is how it is implemented. This includes the fields of the class and the code of its methods. The specification never mentions the representation.
ListQueue's representation: one list of the NetIDs waiting, front first
4* The queue for office hours: the students waiting for help, in
5* the order they joined. A student is named by their NetID, and a
6* student is in the queue at most once.A student is in the queue at most once
join and next can change the queuejoin turns away a student who is already waitingnext only removes, and waiting hands out a new listDefinition: abstract invariant
An abstract invariant of an ADT is a claim about the abstract state that is true whenever no method of the type is running.
ListQueue's join walks the list for a student already waiting6public class LinkedQueue implements HelpQueue {
7 /** One student waiting, and the node of the student after them. */
8 private static class Node {
9 String netid;
10 Node next; // null for the last node in the chain
11
12 Node(String netid) {
13 this.netid = netid;
14 }
15 }
16
17 // The first and last nodes of the chain, both null when nobody
18 // is waiting.
19 private Node front;
20 private Node back;Node is a class declared inside LinkedQueue, and private to it
4ListQueue queue = new ListQueue();4HelpQueue queue = new LinkedQueue();$ javac OfficeHours.java $ java OfficeHours [ada, bob] helping ada
What does it print now?
The same two lines as on a ListQueue
OfficeHours takes either class by changing one lineHelpQueueList is an ADT tooArrayList keeps an array, LinkedList a chain of nodesList: the same methods, the same specificationsList takes either by changing one lineList's abstract state: its elements, in order
Sources: ACM's A.M. Turing Award page for Barbara Liskov; Liskov and Zilles, SIGPLAN Notices 9(4), 1974
9public abstract class HelpQueueTest {
10 // A subclass says which implementation to test.
11 abstract HelpQueue emptyQueue();
12
13 @Test
14 void studentsAreServedInTheOrderTheyJoined() {
15 HelpQueue queue = emptyQueue();Each test gets its queue from emptyQueue
abstract: no body here, so a subclass must give it one
Specification-based tests: they pass on every correct implementation
1public class ListQueueSpecTest extends HelpQueueTest {
2 HelpQueue emptyQueue() {
3 return new ListQueue();
4 }
5}1public class LinkedQueueSpecTest extends HelpQueueTest {
2 HelpQueue emptyQueue() {
3 return new LinkedQueue();
4 }
5}The line that picks the implementation
Five tests each: how many of the ten pass?
$ java -jar junit.jar execute --disable-banner \ --class-path . --select-class ListQueueSpecTest \ --select-class LinkedQueueSpecTest ⋮ │ ├─ ListQueueSpecTest ✔ ⋮ │ └─ LinkedQueueSpecTest ✔ ⋮ [ 10 tests successful ] [ 0 tests failed ]
None of the five can tell the two classes apart
10@Test
11void joinTwiceSaysWho() {
12 HelpQueue queue = new ListQueue();
13 queue.join("ada");
14 IllegalArgumentException e = assertThrows(
15 IllegalArgumentException.class, () -> queue.join("ada"));
16 assertEquals("ada is in the queue", e.getMessage());
17}() -> queue.join("ada"): the call, handed to assertThrows to make
It checks join's message, word for word
Change new ListQueue() to new LinkedQueue(): will the test pass?
$ java -jar junit.jar execute --disable-banner \ --class-path . --select-class ListQueueImplTest ⋮ │ ├─ joinTwiceSaysWho() ✘ expected: <ada is in the queue> but was: <ada is already waiting> ⋮
LinkedQueue is right: HelpQueue never promised those words
Implementation-based: the test relied on more than the specification
In ListQueueImplTest.java, new ListQueue() changed to new LinkedQueue()
join's check out13public void join(String netid) {
14 if (queue.contains(netid)) {
15 throw new IllegalArgumentException(netid + " is in the queue");
16 }
17 queue.add(netid);
18}Which of the five tests fail?
join's check out, the run$ java -jar junit.jar execute --disable-banner \ --class-path . --select-class ListQueueSpecTest ⋮ │ ├─ nobodyIsInTheQueueTwice() ✘ Expected java.lang.IllegalArgumentException to be thrown, but nothing was thrown. ⋮ [ 4 tests successful ] [ 1 tests failed ]
The other four never join a student who is already waiting
ListQueue's waiting returned its field queuewaiting said less28/** @return the students waiting, front first, in a new list */What could a client take that to mean?
HelpQueue promises the first: in a new list
27public List<String> waiting() {
28 // A copy, so that a client cannot change the queue through it.
29 return new ArrayList<String>(queue);29 return queue;
30}Question 2: how did you get ada in twice?
waitingIsACopy, on the queue's own list48@Test
49void waitingIsACopy() {
50 HelpQueue queue = emptyQueue();
51 queue.join("ada");
52 List<String> copy = queue.waiting();
53 copy.add("eve");
54 assertEquals(List.of("ada"), queue.waiting());
55}One of the five tests changes the list waiting gave it
$ java -jar junit.jar execute --disable-banner \ --class-path . --select-class ListQueueSpecTest ⋮ │ ├─ nextOnAnEmptyQueueThrows() ✔ │ ├─ waitingIsACopy() ✘ expected: <[ada]> but was: <[ada, eve]> │ ├─ nobodyIsInTheQueueTwice() ✔ │ ├─ aStudentWhoLeftMayComeBack() ✔ │ └─ studentsAreServedInTheOrderTheyJoined() ✔ ⋮
eve got in without join
The public field's problems, back again
waiting makes a copy27public List<String> waiting() {
28 // A copy, so that a client cannot change the queue through it.
29 return new ArrayList<String>(queue);
30}join and next can change the queue