Forklift Fleet Control at Merchant's Warehouse
Merchant's warehouse has 12 autonomous forklifts moving pallets between 60 marked locations. The control software is one procedural program of 3,000 lines with 47 global variables. Sensor events such as obstacle, pallet detected and aisle entry are polled every 100 ms. A new safety rule says a forklift must give way if another forklift is already in a narrow aisle; the rule changed three times last month. Two developers disagree. One wants to rewrite using object-oriented programming, with a Forklift class holding position, battery and state, and an Aisle class enforcing entry. The other wants a declarative rule engine so that safety rules can be edited as “if aisle occupied then stop” without recompiling. The operations manager is worried about a current bug: two forklifts sometimes enter the same aisle because the check and the move are separate steps. Testing time is limited, and the fleet cannot stop for a long rewrite.
1. Compare object-oriented programming with a declarative rule engine for this system, using the aisle-safety rule as the example. Recommend one approach or a hybrid, and justify your recommendation.
Read the worked answer
OOP models state and behaviour together. A Forklift object holds position, battery and state; an Aisle object holds occupiedBy and exposes an operation such as requestEntry(forklift). Encapsulation stops other code from changing occupiedBy directly. Inheritance can represent different forklift types without duplicating logic. The weakness is that changing a safety rule means editing methods, recompiling and retesting, which is slow when rules change weekly.
A declarative rule engine externalises rules as data: if aisle.occupied and forklift.requesting then stop. A safety officer can change thresholds without code changes, and rules can be inspected. The weakness is that a rule engine does not automatically make check-and-act atomic. If the engine evaluates “aisle free” and then a separate action moves the forklift, the same race remains.
Recommendation: a hybrid. Keep an OOP core for state and atomic operations, especially aisle entry, and put tunable policy rules in a table or small rule engine. The current bug is a concurrency and encapsulation problem, not a syntax problem. A rule engine alone would not fix it unless it provided transactions. The hybrid gives safe state changes and fast rule changes, but it adds a rule-interpretation layer that must be tested.
2. Explain how encapsulation and a class invariant could prevent the double-entry bug. Give a concrete method design and explain why the old polling design is vulnerable.
Read the worked answer
The old design checks a global variable and then moves in separate steps. Between the check and the move, the scheduler can switch to the other forklift, which also sees “aisle free” and moves. This is a check-then-act race; polling every 100 ms does not remove it and adds up to 100 ms of delay.
Concrete design: Aisle has private occupant and private lock. The public operation is boolean requestEntry(Forklift f). Inside one critical section it tests occupant == null; if true it sets occupant = f and returns true, otherwise returns false. releaseEntry(Forklift f) clears the occupant only if f is the current occupant. There is no public setter. The class invariant is: at most one forklift occupies a narrow aisle, and occupant is either null or a forklift physically in that aisle. The forklift calls requestEntry(this) and only moves if the result is true; otherwise it stops and retries later.
Encapsulation makes the invariant enforceable because no other module can bypass the check. The lock or monitor makes the test-and-set atomic. The trade-off is that a held lock must be short; do not keep it while the forklift physically moves, or the system serialises unnecessarily.
3. The team has six weeks and cannot stop the fleet. Propose a migration and test plan that reduces risk while still fixing the aisle bug.
Read the worked answer
Use a strangler migration rather than a rewrite. Week 1: wrap the 47 globals behind accessor functions and add logging, without changing behaviour. Week 2: introduce an Aisle class with requestEntry and releaseEntry, but run it in shadow mode: the old logic still controls the forklift, while the new object records what it would have decided. Compare decisions on live traffic.
Weeks 3–4: unit-test the aisle invariant with concurrent simulated calls, then run the new entry path on one non-critical forklift during a low-traffic shift. Keep a physical emergency stop and a software rollback flag. Week 5: expand to three forklifts, then all 12 if no double entries occur. Week 6: remove the old check-and-move code and keep the logging for audit.
Test cases should include two forklifts requesting the same aisle in the same millisecond, a forklift losing network contact while holding the aisle, and a manual release. The plan is safer than a full OOP rewrite because it keeps the fleet running, isolates the safety-critical change, and produces evidence before full deployment.