Lab #4: Circuits as Objects — Collections and Comparable
Continues the circuit simulator. So far, “a circuit” hasn’t really existed as a thing in your program — it’s just been a loose array of components, held together by a test class. This lab gives it real object status.
Objectives
- Model a whole circuit as its own class, composed of a
Listof components. - Sort objects using
Comparable, and seeCollections.sortput that to work. - Build a filtered result list from a heterogeneous collection using
instanceof. - Use a
Mapto maintain a two-way association table.
1. A Circuit class
Difficulty: Rx
Design a Circuit class along these lines:
- A
Circuitis composed ofComponents through acomponentsfield — use ajava.util.List<Component>for this, not a raw array. - Give
Circuittwo constructors: a no-argumentpublic Circuit()(leavingcomponentsas an empty list andnameunset — you’ll need this one in Lab #5, to build an emptyCircuitbefore callingload(...)on it) and the main one,public Circuit(String name, Component[] parts), which builds a circuit from a name and an array of already-wired components:- add every element of
partsintocomponents(checkList’s bulk-add methods, andArrays.asListfor converting an array into aListview — no need to write a loop by hand), - then sort
componentsby id usingCollections.sort. For this to compile,Componentneeds to implementComparable<Component>, providing acompareTo(Component other)that orders components bygetId().
- add every element of
public List<String> partList()returns the (sorted, sincecomponentsis sorted) list of component ids.public void description()prints the circuit’s name followed by each component’s description.public void traceStates()prints the circuit’s name followed by each component’straceState().public List<Switch> getSwitches()returns the list of the circuit’s switches — build it by walkingcomponentsand testing each one withinstanceof Switch.public List<Valve> getValves()— same idea, for valves.
(This lab treats every circuit input as a plain Switch and every output as a plain Valve, to keep things concrete. A more general design would introduce a common supertype for “any kind of input device” — switches, sensors, dials, … — and similarly for outputs; keep that in mind as a natural next step, not something to build now.)
Test it
In CircuitTest, in // wire (once your example circuit’s components are fully connected), instantiate a Circuit with a name and your array of components. Write static void test(Circuit circuit) that calls, in order: partList() (print the result), description(), getSwitches()/getValves() (print their ids), then flips a few switches and prints the resulting valve states (or the full traceStates()).
2. Probing a whole circuit
2.1 The probe/unprobe protocol
Difficulty: HardProbing an entire circuit means substituting every one of its switches with a LazyProbe, all at once. Add this protocol to Circuit:
public void probe()— perform the substitutions.public void resetProbes()— reset every active probe (via each one’sreset()) for a fresh round.public void unprobe()— undo the substitutions: reconnect the original switches, discard the probes.
2.2 A two-way association table
Difficulty: HardCircuit needs to remember which probe replaced which switch, for two reasons:
- During
probe(): to recognize when a switch has already been substituted (the same switch can legitimately feed more than one input — probing it twice would create duplicate, inconsistent probes). - During
unprobe(): to find the original switch that a given probe should be replaced back with.
A single Map only gets you a lookup in one direction (Probe -> Switch or Switch -> Probe, not both), so keeping two maps in sync is the natural solution here.
Write a ProbeTable class:
- It holds two instance fields,
probeToSwitchandswitchToProbe(bothMaps —HashMaporTreeMap, your choice). Switch getSwitch(LazyProbe probe)returns the switch associated with the given probe.LazyProbe getProbe(Switch s, Component target, String inputName)returns the probe already associated withsif one exists; otherwise it creates one (usingtargetandinputName), records the association in both maps, and returns it.void resetProbes()callsreset()on every probe it holds.void clear()removes all associations (viaclear()on both maps).
2.3 Wiring it into Circuit
Difficulty: Hard
- Give
Circuitaprotected ProbeTable probeTable = new ProbeTable();field. Circuit.probe()should walk its gates, calling avoid probe(ProbeTable table)method that you add — following the same factoring you already did in Lab #3 §2.2, that means adding it toNot(one input) and toTwoInputGate(two inputs, inherited by bothAndandOr), not separately to each concrete gate class. Each implementation checks whether its own input(s) are switches and, if so, updatestableaccordingly. Apply the same idea forunprobe(): given a gate’s current input typed asComponent, checkinstanceof LazyProbe, cast, then callprobeTable.getSwitch(...)to recover the original switch to reconnect.- Test
probe(),resetProbes(), andunprobe()from yourtest(Circuit circuit)method.