Skip to content

Commit 4afabdb

Browse files
committed
docs: update bloc pattern
1 parent 5241a4a commit 4afabdb

File tree

2 files changed

+133
-131
lines changed

2 files changed

+133
-131
lines changed

bloc/README.md

+127-125
Original file line numberDiff line numberDiff line change
@@ -2,174 +2,177 @@
22
title: "Bloc Pattern in Java: State Management Simplified"
33
shortTitle: Bloc
44
description: "Learn how the Bloc pattern helps manage state changes in Java applications. This guide covers dynamic listener management, real-world examples, and clean code practices for state management."
5-
category: Structural
5+
category: Architectural
66
language: en
77
tag:
8-
- Event-driven
8+
- Abstraction
9+
- Data binding
10+
- Decoupling
11+
- Event-driven
12+
- Presentation
13+
- Reactive
14+
- Reusability
15+
- State tracking
916
---
1017

1118
## Also known as
1219

13-
* Event-driven State Management
14-
* State Listener Pattern
20+
* Business Logic Component
21+
* Business Logic Controller
1522

1623
## Intent of the Bloc Pattern
1724

1825
The Bloc pattern manages the state of an object and allows for dynamically notifying interested listeners about state changes. It separates state management logic from the rest of the application, improving code organization and flexibility.
1926

2027
## Detailed explanation of the Bloc pattern with real-World examples
2128

22-
### Real-world example
29+
Real-world example
2330

2431
> Consider a digital counter application where multiple parts of the UI need to be updated whenever the counter changes. For example, a label displaying the counter value and an activity log showing changes. Instead of directly modifying these UI components, the Bloc pattern manages the counter state and notifies all registered listeners about the state change. Listeners can dynamically subscribe or unsubscribe from receiving updates.
2532
26-
### In plain words
33+
In plain words
2734

2835
> The Bloc pattern manages a single state object and dynamically notifies registered listeners whenever the state changes.
2936
30-
### Wikipedia says
37+
Wikipedia says
3138

3239
> While not a formalized "Gang of Four" design pattern, Bloc is widely used in state-driven applications. It centralizes state management and propagates state changes to registered observers, following principles of separation of concerns.
3340
34-
### Sequence diagram
41+
Sequence diagram
3542

3643
![Bloc sequence diagram](./etc/bloc-sequence-diagram.png)
3744

38-
---
39-
4045
## Programmatic Example of the Bloc Pattern in Java
4146

42-
### **Core Components of the Bloc Pattern**
47+
This example demonstrates how to implement the Bloc pattern using Java and Swing. The pattern separates the state of the application from UI components, and provides a reactive, event-driven approach to managing updates.
4348

44-
#### **1. State Object**
49+
Core components of the Bloc Pattern include a `State` object, a `Bloc` class responsible for managing and updating that state, and interfaces (`StateListener` and `ListenerManager`) for subscribing to changes.
4550

46-
The `State` class holds the representation of the state of the application that will be passed to listeners whenever there is a change to do but in this example it's simplified to be a single value.
51+
The `State` class represents the application's data at any given time.
4752

4853
```java
49-
package com.iluwatar.bloc;
50-
51-
import lombok.Getter;
52-
53-
@Getter
54-
public class State {
55-
private final int value;
54+
public record State(int value) {}
55+
```
5656

57-
public State(int value) {
58-
this.value = value;
59-
}
57+
The `ListenerManager` interface declares methods to add and remove listeners, as well as retrieve them.
6058

61-
}
62-
```
63-
The `ListenerManager` interface manages the basic operations for the listeners and is implemented by bloc class
6459
```java
65-
import java.util.List;
66-
6760
public interface ListenerManager<T> {
68-
void addListener(StateListener<T> listener);
69-
void removeListener(StateListener<T> listener);
70-
List<StateListener<T>> getListeners();
61+
void addListener(StateListener<T> listener);
62+
void removeListener(StateListener<T> listener);
63+
List<StateListener<T>> getListeners();
7164
}
7265
```
73-
The `StateListener` interface has a method that the listener needs to react to changes in the state and is used by bloC to notify listeners whenever there is an update to state.
66+
67+
The `StateListener` interface defines how a listener reacts to state changes.
68+
7469
```java
7570
public interface StateListener<T> {
76-
void onStateChange(T state);
71+
void onStateChange(T state);
7772
}
7873
```
7974

80-
The `Bloc` class holds the current state and manages logic of states and notifies the list of listeners when states changes.
81-
The `Bloc` class contains methods for listeners and states like emitstate which updates the currentstate and notifies listeners addlistener which adds new listener to the listeners list and notifies it with the currentstate removelistener which removes listener from the listeners list and increment which increases the state value by 1 which is like an update to the current state and notifies the listeners in listeners list with the new state which holds a value incremented by 1 and decrement functions which does the opposite of increment function and notifies listeners in listeners list.
82-
```java
83-
import java.util.ArrayList;
84-
import java.util.Collections;
85-
import java.util.List;
75+
The `Bloc` class maintains the current state and notifies listeners of updates. It provides `increment` and `decrement` methods to update state and automatically notify registered listeners.
8676

77+
```java
8778
public class Bloc implements ListenerManager<State> {
88-
private State currentState;
89-
private final List<StateListener<State>> listeners = new ArrayList<>();
9079

91-
public Bloc() {
92-
this.currentState = new State(0);
93-
}
80+
private State currentState;
81+
private final List<StateListener<State>> listeners = new ArrayList<>();
82+
83+
public Bloc() {
84+
this.currentState = new State(0);
85+
}
86+
87+
@Override
88+
public void addListener(StateListener<State> listener) {
89+
listeners.add(listener);
90+
listener.onStateChange(currentState);
91+
}
92+
93+
@Override
94+
public void removeListener(StateListener<State> listener) {
95+
listeners.remove(listener);
96+
}
97+
98+
@Override
99+
public List<StateListener<State>> getListeners() {
100+
return Collections.unmodifiableList(listeners);
101+
}
102+
103+
private void emitState(State newState) {
104+
currentState = newState;
105+
for (StateListener<State> listener : listeners) {
106+
listener.onStateChange(currentState);
107+
}
108+
}
94109

95-
@Override
96-
public void addListener(StateListener<State> listener) {
97-
listeners.add(listener);
98-
listener.onStateChange(currentState);
99-
}
110+
public void increment() {
111+
emitState(new State(currentState.value() + 1));
112+
}
100113

101-
@Override
102-
public void removeListener(StateListener<State> listener) {
103-
listeners.remove(listener);
114+
public void decrement() {
115+
emitState(new State(currentState.value() - 1));
116+
}
104117
}
118+
```
105119

106-
@Override
107-
public List<StateListener<State>> getListeners() {
108-
return Collections.unmodifiableList(listeners);
109-
}
120+
This class demonstrates how to integrate the Bloc pattern with a simple Swing GUI. It sets up a counter, buttons to change the state, and a toggle to enable or disable the listener dynamically.
110121

111-
private void emitState(State newState) {
112-
currentState = newState;
113-
for (StateListener<State> listener : listeners) {
114-
listener.onStateChange(currentState);
115-
}
122+
```java
123+
public class Main {
124+
public static void main(String[] args) {
125+
BlocUi blocUi = new BlocUi();
126+
blocUi.createAndShowUi();
127+
}
116128
}
117129

118-
public void increment() {
119-
emitState(new State(currentState.getValue() + 1));
120-
}
130+
public class BlocUi {
121131

122-
public void decrement() {
123-
emitState(new State(currentState.getValue() - 1));
124-
}
125-
}
126-
```
127-
The `main` class have a simple gui to try and test the bloc pattern components separately from the ui components.
128-
the `main` class creates an instance of bloc then adds a listener to update the ui which resembles the counter and some buttons to change the states and toggle the listener dynamically
129-
```java
130-
import javax.swing.*;
131-
import java.awt.*;
132+
public void createAndShowUi() {
133+
final Bloc bloc = new Bloc();
132134

133-
public class Main {
134-
public static void main(String[] args) {
135-
Bloc bloc = new Bloc();
136-
JFrame frame = new JFrame("Bloc Example");
137-
frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
138-
frame.setSize(400, 300);
139-
JLabel counterLabel = new JLabel("Counter: 0", SwingConstants.CENTER);
140-
counterLabel.setFont(new Font("Arial", Font.BOLD, 20));
141-
JButton incrementButton = new JButton("Increment");
142-
JButton decrementButton = new JButton("Decrement");
143-
JButton toggleListenerButton = new JButton("Disable Listener");
135+
JFrame frame = new JFrame("BloC example");
136+
frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
137+
frame.setSize(400, 300);
138+
139+
JLabel counterLabel = new JLabel("Counter: 0", SwingConstants.CENTER);
140+
counterLabel.setFont(new Font("Arial", Font.BOLD, 20));
141+
142+
JButton decrementButton = new JButton("Decrement");
143+
JButton toggleListenerButton = new JButton("Disable Listener");
144+
JButton incrementButton = new JButton("Increment");
144145

145146
frame.setLayout(new BorderLayout());
146147
frame.add(counterLabel, BorderLayout.CENTER);
147148
frame.add(incrementButton, BorderLayout.NORTH);
148149
frame.add(decrementButton, BorderLayout.SOUTH);
149150
frame.add(toggleListenerButton, BorderLayout.EAST);
150151

151-
StateListener<State> stateListener = state -> counterLabel.setText("Counter: " + state.getValue());
152+
StateListener<State> stateListener = state -> counterLabel.setText("Counter: " + state.value());
152153

153154
bloc.addListener(stateListener);
154155

155-
toggleListenerButton.addActionListener(e -> {
156-
if (bloc.getListeners().contains(stateListener)) {
157-
bloc.removeListener(stateListener);
158-
toggleListenerButton.setText("Enable Listener");
159-
} else {
160-
bloc.addListener(stateListener);
161-
toggleListenerButton.setText("Disable Listener");
162-
}
163-
});
156+
toggleListenerButton.addActionListener(
157+
e -> {
158+
if (bloc.getListeners().contains(stateListener)) {
159+
bloc.removeListener(stateListener);
160+
toggleListenerButton.setText("Enable Listener");
161+
} else {
162+
bloc.addListener(stateListener);
163+
toggleListenerButton.setText("Disable Listener");
164+
}
165+
});
164166

165167
incrementButton.addActionListener(e -> bloc.increment());
166168
decrementButton.addActionListener(e -> bloc.decrement());
167169

168170
frame.setVisible(true);
169-
}
171+
}
170172
}
171173
```
172-
## Program Output
174+
175+
### Program Output
173176

174177
- **On Increment**
175178
`Counter: 1`
@@ -181,48 +184,47 @@ JButton toggleListenerButton = new JButton("Disable Listener");
181184
- Listener disabled: Counter stops updating.
182185
- Listener enabled: Counter updates again.
183186

184-
---
185-
186187
## When to Use the Bloc Pattern
187188

188189
Use the Bloc pattern when:
189190

190-
- You need a centralized system to manage state updates.
191-
- You want to dynamically add/remove listeners without tight coupling.
192-
- You are building an event-driven or state-driven system, such as UI frameworks.
193-
---
191+
* When you want a clean separation of business logic and UI in Java applications
192+
* When you need a reactive approach to updating UI based on state changes
193+
* When you want to avoid coupling controllers or presenters directly to data manipulation
194+
* When multiple UI elements need access to the same business logic
194195

195196
## Real-World Applications of Bloc Pattern
196197

197-
- **UI State Management**: Reacting to button clicks, updating labels, and toggling views.
198-
- **Event-driven Systems**: Handling multiple subscribers efficiently for state updates.
199-
---
198+
* Java-based desktop applications that require real-time UI updates
199+
* Backend-driven Java frameworks that separate service layers from presentation
200+
* Cross-platform applications where the logic must remain consistent regardless of the UI technology
200201

201202
## Benefits and Trade-offs of Bloc Pattern
202203

203-
### Benefits:
204-
- Clean separation of state management and UI logic.
205-
- Flexibility to dynamically add/remove listeners.
206-
- Centralized state propagation.
204+
Benefits:
207205

208-
### Trade-offs:
209-
- Adds some complexity with the listener management mechanism.
210-
- May introduce performance concerns with excessive listeners.
211-
- the bloc class handles too many methods which violates the single responsbility principle
212-
---
206+
* Simplifies UI components by removing direct business logic
207+
* Improves testability by isolating state and behavior
208+
* Encourages code reuse by centralizing data flows
209+
* Enhances maintainability through clear separation of concerns
210+
211+
Trade-offs:
212+
213+
* May introduce additional boilerplate code for managing streams or observers
214+
* Requires careful design to avoid a monolithic “god” component
215+
* Demands consistent reactive programming practices to be effective
213216

214217
## Related Patterns
215218

216-
- **Observer**: Bloc is a specialized implementation of the Observer pattern.
217-
- **Mediator**: Bloc centralizes communication and state propagation.
218-
- **cubit**: bloC is more general implementation than cubit
219-
---
219+
- [Observer](https://java-design-patterns.com/patterns/observer/): Bloc is a specialized implementation of the Observer pattern.
220+
- [Mediator](https://java-design-patterns.com/patterns/mediator/): Orchestrates interactions among multiple objects through a central component
221+
- [MVC](https://java-design-patterns.com/patterns/model-view-controller/): Shares the idea of separating concerns between layers
220222

221223
## References and Credits
222224

223-
- [Design Patterns: Elements of Reusable Object-Oriented Software](https://amzn.to/3w0pvKI)
224-
- [Java Swing Documentation](https://docs.oracle.com/javase/tutorial/uiswing/)
225-
- [Event-Driven Programming in Java](https://www.oracle.com/java/)
226-
- [bloC archetecture](https://bloclibrary.dev/architecture/)
227-
- [flutter bloC package](https://pub.dev/documentation/flutter_bloc/latest/)
228-
225+
* [Bloc architecture(bloclibrary.dev)](https://bloclibrary.dev/architecture/)
226+
* [Clean Architecture: A Craftsman's Guide to Software Structure and Design](https://amzn.to/3UoKkaR)
227+
* [Design Patterns: Elements of Reusable Object-Oriented Software](https://amzn.to/3w0pvKI)
228+
* [Effective Java](https://amzn.to/4cGk2Jz)
229+
* [Event-Driven Programming in Java (Oracle)](https://www.oracle.com/java/)
230+
* [Java Swing Documentation (Oracle)](https://docs.oracle.com/javase/tutorial/uiswing/)

bloc/src/test/java/com/iluwatar/bloc/BlocUiTest.java

+6-6
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@
3232
import org.junit.jupiter.api.BeforeEach;
3333
import org.junit.jupiter.api.Test;
3434

35-
public class BlocUiTest {
35+
class BlocUiTest {
3636

3737
private JFrame frame;
3838
private JLabel counterLabel;
@@ -43,7 +43,7 @@ public class BlocUiTest {
4343
private StateListener<State> stateListener;
4444

4545
@BeforeEach
46-
public void setUp() {
46+
void setUp() {
4747
bloc = new Bloc(); // Re-initialize the Bloc for each test
4848

4949
frame = new JFrame("BloC example");
@@ -83,25 +83,25 @@ public void setUp() {
8383
}
8484

8585
@AfterEach
86-
public void tearDown() {
86+
void tearDown() {
8787
frame.dispose();
8888
bloc = new Bloc(); // Reset Bloc state after each test to avoid state carryover
8989
}
9090

9191
@Test
92-
public void testIncrementButton() {
92+
void testIncrementButton() {
9393
simulateButtonClick(incrementButton);
9494
assertEquals("Counter: 1", counterLabel.getText());
9595
}
9696

9797
@Test
98-
public void testDecrementButton() {
98+
void testDecrementButton() {
9999
simulateButtonClick(decrementButton);
100100
assertEquals("Counter: -1", counterLabel.getText());
101101
}
102102

103103
@Test
104-
public void testToggleListenerButton() {
104+
void testToggleListenerButton() {
105105
// Disable listener
106106
simulateButtonClick(toggleListenerButton);
107107
simulateButtonClick(incrementButton);

0 commit comments

Comments
 (0)