Browser docs

Balking

Intent

Balking Pattern is used to prevent an object from executing a certain code if it is in an incomplete or inappropriate state.

Explanation

Real world example

There’s a start-button in a washing machine to initiate the laundry washing. When the washing machine is inactive the button works as expected, but if it’s already washing the button does nothing.

In plain words

Using the balking pattern, a certain code executes only if the object is in particular state.

Wikipedia says

The balking pattern is a software design pattern that only executes an action on an object when the object is in a particular state. For example, if an object reads ZIP files and a calling method invokes a get method on the object when the ZIP file is not open, the object would “balk” at the request.

Programmatic Example

In this example implementation, WashingMachine is an object that has two states in which it can be: ENABLED and WASHING. If the machine is ENABLED, the state changes to WASHING using a thread-safe method. On the other hand, if it already has been washing and any other thread executes wash() it won’t do that and returns without doing anything.

Here are the relevant parts of the WashingMachine class.

 1@Slf4j
 2public class WashingMachine {
 3
 4  private final DelayProvider delayProvider;
 5  private WashingMachineState washingMachineState;
 6
 7  public WashingMachine(DelayProvider delayProvider) {
 8    this.delayProvider = delayProvider;
 9    this.washingMachineState = WashingMachineState.ENABLED;
10  }
11
12  public WashingMachineState getWashingMachineState() {
13    return washingMachineState;
14  }
15
16  public void wash() {
17    synchronized (this) {
18      var machineState = getWashingMachineState();
19      LOGGER.info("{}: Actual machine state: {}", Thread.currentThread().getName(), machineState);
20      if (this.washingMachineState == WashingMachineState.WASHING) {
21        LOGGER.error("Cannot wash if the machine has been already washing!");
22        return;
23      }
24      this.washingMachineState = WashingMachineState.WASHING;
25    }
26    LOGGER.info("{}: Doing the washing", Thread.currentThread().getName());
27    this.delayProvider.executeAfterDelay(50, TimeUnit.MILLISECONDS, this::endOfWashing);
28  }
29
30  public synchronized void endOfWashing() {
31    washingMachineState = WashingMachineState.ENABLED;
32    LOGGER.info("{}: Washing completed.", Thread.currentThread().getId());
33  }
34}

Here’s the simple DelayProvider interface used by the WashingMachine.

1public interface DelayProvider {
2  void executeAfterDelay(long interval, TimeUnit timeUnit, Runnable task);
3}

Now we introduce the application using the WashingMachine.

 1  public static void main(String... args) {
 2    final var washingMachine = new WashingMachine();
 3    var executorService = Executors.newFixedThreadPool(3);
 4    for (int i = 0; i < 3; i++) {
 5      executorService.execute(washingMachine::wash);
 6    }
 7    executorService.shutdown();
 8    try {
 9      executorService.awaitTermination(10, TimeUnit.SECONDS);
10    } catch (InterruptedException ie) {
11      LOGGER.error("ERROR: Waiting on executor service shutdown!");
12      Thread.currentThread().interrupt();
13    }
14  }

Here is the console output of the program.

14:02:52.268 [pool-1-thread-2] INFO com.iluwatar.balking.WashingMachine - pool-1-thread-2: Actual machine state: ENABLED
14:02:52.272 [pool-1-thread-2] INFO com.iluwatar.balking.WashingMachine - pool-1-thread-2: Doing the washing
14:02:52.272 [pool-1-thread-3] INFO com.iluwatar.balking.WashingMachine - pool-1-thread-3: Actual machine state: WASHING
14:02:52.273 [pool-1-thread-3] ERROR com.iluwatar.balking.WashingMachine - Cannot wash if the machine has been already washing!
14:02:52.273 [pool-1-thread-1] INFO com.iluwatar.balking.WashingMachine - pool-1-thread-1: Actual machine state: WASHING
14:02:52.273 [pool-1-thread-1] ERROR com.iluwatar.balking.WashingMachine - Cannot wash if the machine has been already washing!
14:02:52.324 [pool-1-thread-2] INFO com.iluwatar.balking.WashingMachine - 14: Washing completed.

Class diagram

alt text

Applicability

Use the Balking pattern when

  • You want to invoke an action on an object only when it is in a particular state
  • Objects are generally only in a state that is prone to balking temporarily but for an unknown amount of time

Credits