The current design ties the remote control tightly to the devices (the light and the air conditioner). The remote calls the device methods itself. It also undoes actions by remembering a loose text string. The design becomes rigid and hard to extend.
1. Understanding the Problem
Imagine a simple home automation remote where button presses switch devices like a Light or an AC on and off.
In a simple first version, the remote control holds a real object for every appliance type. When a button is pressed, the remote itself must call the right method on the right device. So the remote and the devices are tied together.
2. Real-Life Analogy
Think of a television remote control. When you press "Volume Up", the remote cannot change the sound itself. It only sends a signal. It does not care if the receiver is a soundbar, an old TV or a projector, as long as the receiver understands the signal.
Our simple first design breaks this idea. The remote must know exactly how every appliance in the room works.
3. Four Key Components at Risk
To see why this code is fragile, look at how it misuses the four key components of the Command Pattern:
- Client: The setup code must connect real device classes straight into the remote, fixed when the code is compiled.
- Invoker (
NaiveRemoteControl): It grows large, because it needs a separate method for every action. - Command: Missing. Nothing represents the "request" as its own object. The request is only code inside the remote class.
- Receiver (
Light/AC): Open to the invoker, which must know its exact methods (on(),off()).
4. Architectural Deficiencies in the Naive Code
Tight Coupling
The NaiveRemoteControl class holds direct references to Light and AC. To add a new appliance (e.g., a SmartTV or a Geyser), you must edit the remote control class. You add new private fields and change the constructor. This breaks the Open/Closed Principle (OCP): code should be open for new features but closed to edits of working code.
Lack of Flexibility & Hardcoded Commands
Each command is a fixed method like pressLightOn() or pressACOn(). The setup is static. You cannot give a button a different action while the program runs. You also cannot easily make one button run a series of actions (a macro), such as a "Leave Home" button that switches off every appliance.
Primitive Undo Functionality
The pressUndo() method uses a fragile switch block that compares strings ("LIGHT_ON", "AC_OFF"). The remote must work out how to reverse each action. This is easy to get wrong, and the switch grows with every new appliance.
Inability to Maintain True Command History
The remote remembers only one string variable (lastAction), so it keeps a history of just one step. Worse, pressUndo() sets lastAction to the opposite action after it reverses something. So a second undo does not go back a step. It undoes the first undo and redoes the action. In Main, after LIGHT_ON, AC_ON, LIGHT_OFF, the first undo turns the light on, and the second undo turns it off again. The AC is never reached. Real multi-level undo is impossible to build this way.
5. Structural Breakdown of Vulnerabilities
| Operational Metric | Naive Design Status | Architectural Target |
|---|---|---|
| Extensibility | Low: Modifying hardware requires updating the remote console. | High: Plug new devices in via uniform abstractions. |
| Undo Support | Fragile: Done with string checks inside switches. | Strong: Command objects carry their own reversal logic. |
| History Retention | Shallow: Remembers only the very last change. | Deep: A growing history stack keeps every step. |
| SRP Compliance | Violated: The remote handles clicks and the rules for reversing devices. | Followed: The remote handles triggers; commands handle logic. |
6. Summary of the "Smell"
When the part that triggers an action must know the exact details of the class that carries it out, the request has not been wrapped in its own object.
The Command Design Pattern fixes this. It turns the request into its own object with its own execute() and undo() methods. All commands share one interface (polymorphism). The trigger and the receiver are then fully separate.