Skip to main content
Object Oriented Software Design
Object Oriented Software Design

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 List of components.
  • Sort objects using Comparable, and see Collections.sort put that to work.
  • Build a filtered result list from a heterogeneous collection using instanceof.
  • Use a Map to maintain a two-way association table.

1. A Circuit class

Difficulty: Rx

Design a Circuit class along these lines:

  • A Circuit is composed of Components through a components field — use a java.util.List<Component> for this, not a raw array.
  • Give Circuit two constructors: a no-argument public Circuit() (leaving components as an empty list and name unset — you’ll need this one in Lab #5, to build an empty Circuit before calling load(...) 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 parts into components (check List’s bulk-add methods, and Arrays.asList for converting an array into a List view — no need to write a loop by hand),
    • then sort components by id using Collections.sort. For this to compile, Component needs to implement Comparable<Component>, providing a compareTo(Component other) that orders components by getId().
  • public List<String> partList() returns the (sorted, since components is 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’s traceState().
  • public List<Switch> getSwitches() returns the list of the circuit’s switches — build it by walking components and testing each one with instanceof 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: Hard

Probing 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’s reset()) 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: Hard

Circuit 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, probeToSwitch and switchToProbe (both Maps — HashMap or TreeMap, 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 with s if one exists; otherwise it creates one (using target and inputName), records the association in both maps, and returns it.
  • void resetProbes() calls reset() on every probe it holds.
  • void clear() removes all associations (via clear() on both maps).

2.3 Wiring it into Circuit

Difficulty: Hard
  • Give Circuit a protected ProbeTable probeTable = new ProbeTable(); field.
  • Circuit.probe() should walk its gates, calling a void probe(ProbeTable table) method that you add — following the same factoring you already did in Lab #3 §2.2, that means adding it to Not (one input) and to TwoInputGate (two inputs, inherited by both And and Or), not separately to each concrete gate class. Each implementation checks whether its own input(s) are switches and, if so, updates table accordingly. Apply the same idea for unprobe(): given a gate’s current input typed as Component, check instanceof LazyProbe, cast, then call probeTable.getSwitch(...) to recover the original switch to reconnect.
  • Test probe(), resetProbes(), and unprobe() from your test(Circuit circuit) method.