Low Level Design
Aug 19, 20263 min read

State Design Pattern

Deep dive into the State Design Pattern with real-world examples

The State Pattern is ideal for scenarios where an object's behaviour changes dramatically based on its internal state. This is like a real life example wherein you are using something which is related to cards and sometimes you have some invalid card or a card is blocked. So that's where you get the transaction to be declined. So this is where the state pattern is used in card system design or transactional system design. It extracts state-specific behaviours into separate state classes. The main context object Card maintains a reference to its state object and delegates requests to it.

State: The Conceptual Model:

Conceptual Model
Conceptual Model
The diagram shows three main states: Active, Blocked, and Reissued. Arrows indicate allowed transitions (e.g., from Active, a card can be Blocked). Notice that Reissued is a terminal state; you cannot directly unblock or re-activate a reissued card.

State: Initial Active State Example

Active State Example
Active State Example
java.terminal
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
// State: Active
class ActiveState implements CardState {
@Override
public void processTransaction(Card card) {
System.out.println("Active: Transaction approved.");
}
@Override
public void blockCard(Card card) {
System.out.println("Active: Blocking the card...");
card.setState(new BlockedState());
}
@Override
public void reissueCard(Card card) {
System.out.println("Active: Direct reissue not allowed. Block the card first.");
}
}
// State: Blocked
class BlockedState implements CardState {
@Override
public void processTransaction(Card card) {
System.out.println("Blocked: Transaction declined. Card is inactive.");
}
@Override
public void blockCard(Card card) {
System.out.println("Blocked: Card is already blocked.");
}
@Override
public void reissueCard(Card card) {
System.out.println("Blocked: Reissuing new card...");
card.setState(new ReissuedState());
}
}
// State: Reissued (Terminal State)
class ReissuedState implements CardState {
@Override
public void processTransaction(Card card) {
System.out.println("Reissued: Transaction declined. This card is permanently deactivated.");
}
@Override
public void blockCard(Card card) {
System.out.println("Reissued: Cannot block a reissued card.");
}
@Override
public void reissueCard(Card card) {
System.out.println("Reissued: Card already reissued.");
}
}
➜~java
UTF-855 lines● ready
State: Transactions to Blocked and Reissued
State Transactions
State Transactions
Following the lifecycle defined in Image 0 and the code structure from Image 1, we now implement the remaining states: BlockedState and ReissuedState.

This new diagram emphasizes how the logic pivots within the pattern. While the Card object remains the same, the behavior completely shifts when the underlying state object changes from ActiveState to BlockedState. We implement the Decline Transaction and Reissue Card transitions identified in the original model.