Skip to content

Commit 21115f4

Browse files
authored
feat: implemented the bloC design pattern issue #2374 (#3128)
* added bloC design pattern * added bloC design pattern * added Readme file * fixed checkstyle warnings * added tests for the ui * fixed a test in MainTest file * separating ui from main file and adding more tests * added pom.xml plugins and properties and fixed readme.md * fixed renaming problem and added context to main * chsnged state class to record * syncing changes for conflicts * Revert "fixed conflicts" * restored files * renamed readme file and abstracted pom file
1 parent 3a1a714 commit 21115f4

File tree

14 files changed

+733
-0
lines changed

14 files changed

+733
-0
lines changed

Diff for: bloc/README.md

+228
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,228 @@
1+
---
2+
title: "Bloc Pattern in Java: State Management Simplified"
3+
shortTitle: Bloc
4+
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
6+
language: en
7+
tag:
8+
- State Management
9+
- Event-driven
10+
- Listener Management
11+
- Object Composition
12+
- Dynamic Behavior
13+
---
14+
15+
## Also known as
16+
17+
* Event-driven State Management
18+
* State Listener Pattern
19+
20+
## Intent of the Bloc Pattern
21+
22+
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.
23+
24+
## Detailed explanation of the Bloc pattern with real-World examples
25+
26+
### Real-world example
27+
28+
> 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.
29+
30+
### In plain words
31+
32+
> The Bloc pattern manages a single state object and dynamically notifies registered listeners whenever the state changes.
33+
34+
### Wikipedia says
35+
36+
> 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.
37+
38+
---
39+
40+
## Programmatic Example of the Bloc Pattern in Java
41+
42+
### **Core Components of the Bloc Pattern**
43+
44+
#### **1. State Object**
45+
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.
47+
48+
```java
49+
package com.iluwatar.bloc;
50+
51+
import lombok.Getter;
52+
53+
@Getter
54+
public class State {
55+
private final int value;
56+
57+
public State(int value) {
58+
this.value = value;
59+
}
60+
61+
}
62+
```
63+
The `ListenerManager` interface manages the basic operations for the listeners and is implemented by bloc class
64+
```java
65+
import java.util.List;
66+
67+
public interface ListenerManager<T> {
68+
void addListener(StateListener<T> listener);
69+
void removeListener(StateListener<T> listener);
70+
List<StateListener<T>> getListeners();
71+
}
72+
```
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.
74+
```java
75+
public interface StateListener<T> {
76+
void onStateChange(T state);
77+
}
78+
```
79+
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;
86+
87+
public class Bloc implements ListenerManager<State> {
88+
private State currentState;
89+
private final List<StateListener<State>> listeners = new ArrayList<>();
90+
91+
public Bloc() {
92+
this.currentState = new State(0);
93+
}
94+
95+
@Override
96+
public void addListener(StateListener<State> listener) {
97+
listeners.add(listener);
98+
listener.onStateChange(currentState);
99+
}
100+
101+
@Override
102+
public void removeListener(StateListener<State> listener) {
103+
listeners.remove(listener);
104+
}
105+
106+
@Override
107+
public List<StateListener<State>> getListeners() {
108+
return Collections.unmodifiableList(listeners);
109+
}
110+
111+
private void emitState(State newState) {
112+
currentState = newState;
113+
for (StateListener<State> listener : listeners) {
114+
listener.onStateChange(currentState);
115+
}
116+
}
117+
118+
public void increment() {
119+
emitState(new State(currentState.getValue() + 1));
120+
}
121+
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+
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");
144+
145+
frame.setLayout(new BorderLayout());
146+
frame.add(counterLabel, BorderLayout.CENTER);
147+
frame.add(incrementButton, BorderLayout.NORTH);
148+
frame.add(decrementButton, BorderLayout.SOUTH);
149+
frame.add(toggleListenerButton, BorderLayout.EAST);
150+
151+
StateListener<State> stateListener = state -> counterLabel.setText("Counter: " + state.getValue());
152+
153+
bloc.addListener(stateListener);
154+
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+
});
164+
165+
incrementButton.addActionListener(e -> bloc.increment());
166+
decrementButton.addActionListener(e -> bloc.decrement());
167+
168+
frame.setVisible(true);
169+
}
170+
}
171+
```
172+
## Program Output
173+
174+
- **On Increment**
175+
`Counter: 1`
176+
177+
- **On Decrement**
178+
`Counter: 0`
179+
180+
- **Dynamic Listener Toggle**
181+
- Listener disabled: Counter stops updating.
182+
- Listener enabled: Counter updates again.
183+
184+
---
185+
186+
## When to Use the Bloc Pattern
187+
188+
Use the Bloc pattern when:
189+
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+
---
194+
195+
## Real-World Applications of Bloc Pattern
196+
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+
---
200+
201+
## Benefits and Trade-offs of Bloc Pattern
202+
203+
### Benefits:
204+
- Clean separation of state management and UI logic.
205+
- Flexibility to dynamically add/remove listeners.
206+
- Centralized state propagation.
207+
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+
---
213+
214+
## Related Patterns
215+
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+
---
220+
221+
## References and Credits
222+
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+

Diff for: bloc/etc/bloc.png

65.5 KB
Loading

Diff for: bloc/etc/bloc.puml

+41
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
@startuml
2+
package com.iluwatar.bloc {
3+
4+
class State {
5+
- value : int
6+
+ State(value : int)
7+
+ getValue() : int
8+
}
9+
10+
interface StateListener<T> {
11+
+ onStateChange(state : T)
12+
}
13+
14+
interface ListenerManager<T> {
15+
+ addListener(listener : StateListener<T>)
16+
+ removeListener(listener : StateListener<T>)
17+
+ getListeners() : List<StateListener<T>>
18+
}
19+
20+
class BloC {
21+
- currentState : State
22+
- listeners : List<StateListener<State>>
23+
+ BloC()
24+
+ addListener(listener : StateListener<State>)
25+
+ removeListener(listener : StateListener<State>)
26+
+ getListeners() : List<StateListener<State>>
27+
- emitState(newState : State)
28+
+ increment()
29+
+ decrement()
30+
}
31+
32+
class Main {
33+
+ main(args : String[])
34+
}
35+
36+
ListenerManager <|.. BloC
37+
StateListener <|.. BloC
38+
BloC o-- State
39+
BloC *-- StateListener
40+
}
41+
@enduml

Diff for: bloc/pom.xml

+60
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<project xmlns="http://maven.apache.org/POM/4.0.0"
3+
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
4+
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
5+
<modelVersion>4.0.0</modelVersion>
6+
<parent>
7+
<groupId>com.iluwatar</groupId>
8+
<artifactId>java-design-patterns</artifactId>
9+
<version>1.26.0-SNAPSHOT</version>
10+
</parent>
11+
<artifactId>bloc</artifactId>
12+
<dependencies>
13+
<dependency>
14+
<groupId>org.junit.jupiter</groupId>
15+
<artifactId>junit-jupiter-api</artifactId>
16+
<scope>test</scope>
17+
</dependency>
18+
<dependency>
19+
<groupId>org.testng</groupId>
20+
<artifactId>testng</artifactId>
21+
<version>7.7.1</version>
22+
<scope>test</scope>
23+
</dependency>
24+
<dependency>
25+
<groupId>org.assertj</groupId>
26+
<artifactId>assertj-core</artifactId>
27+
<version>3.24.2</version>
28+
<scope>test</scope>
29+
</dependency>
30+
<dependency>
31+
<groupId>org.mockito</groupId>
32+
<artifactId>mockito-inline</artifactId>
33+
<scope>test</scope>
34+
</dependency>
35+
<dependency>
36+
<groupId>junit</groupId>
37+
<artifactId>junit</artifactId>
38+
<scope>test</scope>
39+
</dependency>
40+
</dependencies>
41+
<build>
42+
<plugins>
43+
<plugin>
44+
<groupId>org.apache.maven.plugins</groupId>
45+
<artifactId>maven-assembly-plugin</artifactId>
46+
<executions>
47+
<execution>
48+
<configuration>
49+
<archive>
50+
<manifest>
51+
<mainClass>com.iluwatar.bloc.Main</mainClass>
52+
</manifest>
53+
</archive>
54+
</configuration>
55+
</execution>
56+
</executions>
57+
</plugin>
58+
</plugins>
59+
</build>
60+
</project>

0 commit comments

Comments
 (0)