Behavioral
Who is responsible for what, and how objects talk to each other.
Behavioral problems show up as long conditionals, classes that call everyone directly, and logic that has to be repeated whenever something new is added. The code works, but each new case means opening the same method again.
The refactorings here turn actions, rules and states into objects of their own. Once a rule is an object, it can be swapped, queued, undone, chained or told about events, without the code that uses it changing.
You'll recognise it when
- A method grows a new else-if for every new case.
- One class calls five others directly whenever something happens.
- Behaviour depends on a status field checked in many methods.
- Undo, retry or logging is bolted on with a switch over action names.
Example
A smart-home app runs scenes such as "Movie night" and must be able to undo them.
void run(String scene) {
switch (scene) {
case "movie" -> { lights.dim(20); blinds.close(); tv.on(); }
case "away" -> { lights.off(); thermostat.set(16); }
}
history.add(scene);
}
void undo() {
String last = history.removeLast();
switch (last) { // must mirror run() exactly, by hand
case "movie" -> { lights.dim(100); blinds.open(); tv.off(); }
case "away" -> { lights.on(); thermostat.set(21); }
}
}interface SceneAction {
void apply();
void revert();
}
class MovieNight implements SceneAction {
public void apply() { lights.dim(20); blinds.close(); tv.on(); }
public void revert() { lights.dim(100); blinds.open(); tv.off(); }
}
void run(SceneAction action) {
action.apply();
history.push(action);
}
void undo() { history.pop().revert(); }Why it's better: Each scene keeps its "do" and "undo" side by side, so they can't drift apart, and adding a scene doesn't touch run() or undo().
Ideas you'll practise
StrategyObserverStateCommandChain of ResponsibilityTemplate MethodIteratorMediatorMementoVisitor
Problems 29
- BH-19Price Change Calls Everyone by HandL1EasyFREE
- BH-29Shipping Cost If/Else LadderL1EasyFREE
- BH-34A Strategy With One StrategyL3EasyFREE
- BH-01Expense Approval If-ChainL1EasyComing soon
- BH-02Support Ticket EscalationL1MediumComing soon
- BH-03Request Middleware in One MethodL1MediumComing soon
- BH-04A Chain Where Everyone Must RunL2MediumComing soon
- BH-05Undo Built on a Giant SwitchL1MediumComing soon
- BH-06Buttons That Know Too MuchL1EasyComing soon
- BH-07A Job Queue of StringsL1MediumComing soon
- BH-08A Command Class for Every LambdaL3EasyComing soon
- BH-11Reaching Into the Playlist's ArrayL1EasyComing soon
- BH-12Tree Traversal Copy-PastedL1MediumComing soon
- BH-13Every Form Field Knows Every OtherL1MediumComing soon
- BH-14Chat Users Messaging Each Other DirectlyL1MediumComing soon
- BH-16Observer Cascade ChaosL2HardComing soon
- BH-17Public Fields for UndoL1MediumComing soon
- BH-18Game Save That Breaks EncapsulationL1MediumComing soon
- BH-20Polling for Order StatusL1MediumComing soon
- BH-22Listeners That Never UnsubscribeL2MediumComing soon
- BH-24Vending Machine If-StatesL1MediumComing soon
- BH-26Traffic Light Switch StatementsL1EasyComing soon
- BH-27States Implemented as StrategiesL2HardComing soon
- BH-28A State Machine for On/OffL3EasyComing soon
- BH-30Discount Rules HardcodedL1EasyComing soon
- BH-33Template Method Subclass ExplosionL2MediumComing soon
- BH-35Three Report Generators, 90% IdenticalL1MediumComing soon
- BH-36Data Importers Copy-PastedL1MediumComing soon
- BH-38instanceof for Every Export FormatL1HardComing soon