From 3a665b962a3072f82db28f4ab25e232cd9135334 Mon Sep 17 00:00:00 2001 From: darkhyper24 Date: Sat, 7 Dec 2024 16:34:56 +0200 Subject: [PATCH 01/14] added bloC design pattern --- bloC/Readme.md | 0 bloC/etc/bloC.png | Bin 0 -> 67095 bytes bloC/etc/bloC.puml | 41 +++++++++++++ bloC/pom.xml | 27 +++++++++ .../src/main/java/com/iluwatar/bloc/Bloc.java | 45 ++++++++++++++ .../com/iluwatar/bloc/ListenerManager.java | 9 +++ .../src/main/java/com/iluwatar/bloc/Main.java | 45 ++++++++++++++ .../main/java/com/iluwatar/bloc/State.java | 15 +++++ .../java/com/iluwatar/bloc/StateListener.java | 6 ++ .../test/java/com/iluwatar/bloc/BlocTest.java | 56 ++++++++++++++++++ pom.xml | 2 + 11 files changed, 246 insertions(+) create mode 100644 bloC/Readme.md create mode 100644 bloC/etc/bloC.png create mode 100644 bloC/etc/bloC.puml create mode 100644 bloC/pom.xml create mode 100644 bloC/src/main/java/com/iluwatar/bloc/Bloc.java create mode 100644 bloC/src/main/java/com/iluwatar/bloc/ListenerManager.java create mode 100644 bloC/src/main/java/com/iluwatar/bloc/Main.java create mode 100644 bloC/src/main/java/com/iluwatar/bloc/State.java create mode 100644 bloC/src/main/java/com/iluwatar/bloc/StateListener.java create mode 100644 bloC/src/test/java/com/iluwatar/bloc/BlocTest.java diff --git a/bloC/Readme.md b/bloC/Readme.md new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/bloC/etc/bloC.png b/bloC/etc/bloC.png new file mode 100644 index 0000000000000000000000000000000000000000..60d6eb77c8fc5e48a8538ed9e613e29a2d3be34d GIT binary patch literal 67095 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 literal 0 HcmV?d00001 diff --git a/bloC/etc/bloC.puml b/bloC/etc/bloC.puml new file mode 100644 index 000000000000..5991f533ae70 --- /dev/null +++ b/bloC/etc/bloC.puml @@ -0,0 +1,41 @@ +@startuml +package com.iluwatar.bloc { + + class State { + - value : int + + State(value : int) + + getValue() : int + } + + interface StateListener { + + onStateChange(state : T) + } + + interface ListenerManager { + + addListener(listener : StateListener) + + removeListener(listener : StateListener) + + getListeners() : List> + } + + class BloC { + - currentState : State + - listeners : List> + + BloC() + + addListener(listener : StateListener) + + removeListener(listener : StateListener) + + getListeners() : List> + - emitState(newState : State) + + increment() + + decrement() + } + + class Main { + + main(args : String[]) + } + + ListenerManager <|.. BloC + StateListener <|.. BloC + BloC o-- State + BloC *-- StateListener +} +@enduml diff --git a/bloC/pom.xml b/bloC/pom.xml new file mode 100644 index 000000000000..d5fd2c87ee69 --- /dev/null +++ b/bloC/pom.xml @@ -0,0 +1,27 @@ + + + 4.0.0 + + com.iluwatar + java-design-patterns + 1.26.0-SNAPSHOT + + + bloC + + + 17 + 17 + UTF-8 + + + + org.junit.jupiter + junit-jupiter-api + test + + + + \ No newline at end of file diff --git a/bloC/src/main/java/com/iluwatar/bloc/Bloc.java b/bloC/src/main/java/com/iluwatar/bloc/Bloc.java new file mode 100644 index 000000000000..154b9438538a --- /dev/null +++ b/bloC/src/main/java/com/iluwatar/bloc/Bloc.java @@ -0,0 +1,45 @@ +package com.iluwatar.bloc; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +public class Bloc implements ListenerManager { + private State currentState; + private final List> listeners = new ArrayList<>(); + + public Bloc() { + this.currentState = new State(0); + } + + @Override + public void addListener(StateListener listener) { + listeners.add(listener); + listener.onStateChange(currentState); + } + + @Override + public void removeListener(StateListener listener) { + listeners.remove(listener); + } + + @Override + public List> getListeners() { + return Collections.unmodifiableList(listeners); + } + + private void emitState(State newState) { + currentState = newState; + for (StateListener listener : listeners) { + listener.onStateChange(currentState); + } + } + + public void increment() { + emitState(new State(currentState.getValue() + 1)); + } + + public void decrement() { + emitState(new State(currentState.getValue() - 1)); + } +} + diff --git a/bloC/src/main/java/com/iluwatar/bloc/ListenerManager.java b/bloC/src/main/java/com/iluwatar/bloc/ListenerManager.java new file mode 100644 index 000000000000..8643152237aa --- /dev/null +++ b/bloC/src/main/java/com/iluwatar/bloc/ListenerManager.java @@ -0,0 +1,9 @@ +package com.iluwatar.bloc; +import java.util.List; + +public interface ListenerManager { + void addListener(StateListener listener); + void removeListener(StateListener listener); + List> getListeners(); +} + diff --git a/bloC/src/main/java/com/iluwatar/bloc/Main.java b/bloC/src/main/java/com/iluwatar/bloc/Main.java new file mode 100644 index 000000000000..dc6f28fece5c --- /dev/null +++ b/bloC/src/main/java/com/iluwatar/bloc/Main.java @@ -0,0 +1,45 @@ +package com.iluwatar.bloc; +import javax.swing.*; +import java.awt.*; + +public class Main +{ + public static void main(String[] args) + { + Bloc bloC = new Bloc(); + JFrame frame = new JFrame("BloC example"); + frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE); + frame.setSize(400, 300); + JLabel counterLabel = new JLabel("Counter: 0", SwingConstants.CENTER); + counterLabel.setFont(new Font("Arial", Font.BOLD, 20)); + JButton incrementButton = new JButton("Increment"); + JButton decrementButton = new JButton("Decrement"); + JButton toggleListenerButton = new JButton("Disable Listener"); + frame.setLayout(new BorderLayout()); + frame.add(counterLabel, BorderLayout.CENTER); + frame.add(incrementButton, BorderLayout.NORTH); + frame.add(decrementButton, BorderLayout.SOUTH); + frame.add(toggleListenerButton, BorderLayout.EAST); + + StateListener stateListener = state -> counterLabel.setText("Counter: " + state.getValue()); + + bloC.addListener(stateListener); + + toggleListenerButton.addActionListener(e -> + { + if (bloC.getListeners().contains(stateListener)) + { + bloC.removeListener(stateListener); + toggleListenerButton.setText("Enable Listener"); + } else + { + bloC.addListener(stateListener); + toggleListenerButton.setText("Disable Listener"); + } + } + ); + incrementButton.addActionListener(e -> bloC.increment()); + decrementButton.addActionListener(e -> bloC.decrement()); + frame.setVisible(true); + } +} diff --git a/bloC/src/main/java/com/iluwatar/bloc/State.java b/bloC/src/main/java/com/iluwatar/bloc/State.java new file mode 100644 index 000000000000..63ca7b76368b --- /dev/null +++ b/bloC/src/main/java/com/iluwatar/bloc/State.java @@ -0,0 +1,15 @@ +package com.iluwatar.bloc; + +public class State +{ + private final int value; + + public State(int value) + { + this.value = value; + } + public int getValue() + { + return value; + } +} diff --git a/bloC/src/main/java/com/iluwatar/bloc/StateListener.java b/bloC/src/main/java/com/iluwatar/bloc/StateListener.java new file mode 100644 index 000000000000..49cf411fc40e --- /dev/null +++ b/bloC/src/main/java/com/iluwatar/bloc/StateListener.java @@ -0,0 +1,6 @@ +package com.iluwatar.bloc; + +public interface StateListener +{ + void onStateChange(T state); +} diff --git a/bloC/src/test/java/com/iluwatar/bloc/BlocTest.java b/bloC/src/test/java/com/iluwatar/bloc/BlocTest.java new file mode 100644 index 000000000000..d3ef70b63ad3 --- /dev/null +++ b/bloC/src/test/java/com/iluwatar/bloc/BlocTest.java @@ -0,0 +1,56 @@ +package com.iluwatar.bloc; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import java.util.concurrent.atomic.AtomicInteger; +import static org.junit.jupiter.api.Assertions.*; + +class BlocTest { + private Bloc bloc; + private AtomicInteger stateValue; + @BeforeEach + void setUp() { + bloc = new Bloc(); + stateValue = new AtomicInteger(0); + } + @Test + void initialState() { + assertTrue(bloc.getListeners().isEmpty(), "No listeners should be present initially."); + } + + @Test + void IncrementUpdateState() { + bloc.addListener(state -> stateValue.set(state.getValue())); + bloc.increment(); + assertEquals(1, stateValue.get(), "State should increment to 1"); + } + + @Test + void DecrementUpdateState() { + bloc.addListener(state -> stateValue.set(state.getValue())); + bloc.decrement(); + assertEquals(-1, stateValue.get(), "State should decrement to -1"); + } + + @Test + void addingListener() { + bloc.addListener(state -> {}); + assertEquals(1, bloc.getListeners().size(), "Listener count should be 1."); + } + + @Test + void removingListener() { + StateListener listener = state -> {}; + bloc.addListener(listener); + bloc.removeListener(listener); + assertTrue(bloc.getListeners().isEmpty(), "Listener count should be 0 after removal."); + } + @Test + void multipleListeners() { + AtomicInteger secondValue = new AtomicInteger(); + bloc.addListener(state -> stateValue.set(state.getValue())); + bloc.addListener(state -> secondValue.set(state.getValue())); + bloc.increment(); + assertEquals(1, stateValue.get(), "First listener should receive state 1."); + assertEquals(1, secondValue.get(), "Second listener should receive state 1."); + } +} diff --git a/pom.xml b/pom.xml index ef3a39265d53..7b90c33ab1a8 100644 --- a/pom.xml +++ b/pom.xml @@ -218,6 +218,8 @@ function-composition microservices-distributed-tracing microservices-idempotent-consumer + bloC + From 826a067520dc2daf1bddcd8a82ad50324ba612b8 Mon Sep 17 00:00:00 2001 From: darkhyper24 Date: Sat, 7 Dec 2024 16:37:45 +0200 Subject: [PATCH 02/14] added bloC design pattern --- bloC/Readme.md | 1 + 1 file changed, 1 insertion(+) diff --git a/bloC/Readme.md b/bloC/Readme.md index e69de29bb2d1..2f259b79aa7e 100644 --- a/bloC/Readme.md +++ b/bloC/Readme.md @@ -0,0 +1 @@ +s \ No newline at end of file From ede5f1ddf238520879cff0cb8de0075d1ad4b365 Mon Sep 17 00:00:00 2001 From: darkhyper24 Date: Sat, 7 Dec 2024 18:16:40 +0200 Subject: [PATCH 03/14] added Readme file --- bloC/Readme.md | 235 ++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 234 insertions(+), 1 deletion(-) diff --git a/bloC/Readme.md b/bloC/Readme.md index 2f259b79aa7e..aad696868589 100644 --- a/bloC/Readme.md +++ b/bloC/Readme.md @@ -1 +1,234 @@ -s \ No newline at end of file +--- +title: "Bloc Pattern in Java: State Management Simplified" +shortTitle: Bloc +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." +category: Structural +language: en +tag: + - State Management + - Event-driven + - Listener Management + - Object Composition + - Dynamic Behavior +--- + +## Also known as + +* Event-driven State Management +* State Listener Pattern + +## Intent of the Bloc Pattern + +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. + +## Detailed explanation of the Bloc pattern with real-World examples + +### Real-world example + +> 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. + +### In plain words + +> The Bloc pattern manages a single state object and dynamically notifies registered listeners whenever the state changes. + +### Wikipedia says + +> 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. + +--- + +## Programmatic Example of the Bloc Pattern in Java + +### 1. `Troll` Analogy Using Bloc + +Imagine a simple state-driven system that maintains a counter. Whenever the counter changes, any registered listener receives updates dynamically. + +--- + +### **Core Components of the Bloc Pattern** + +#### **1. State Object** + +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. + +```java +package com.iluwatar.bloc; + +public class State { + private final int value; + + public State(int value) { + this.value = value; + } + + public int getValue() { + return value; + } +} +``` +The `ListenerManager` interface manages the basic operations for the listeners and is implemented by bloc class +```java +import java.util.List; + +public interface ListenerManager { + void addListener(StateListener listener); + void removeListener(StateListener listener); + List> getListeners(); +} +``` +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. +```java +public interface StateListener { +void onStateChange(T state); +} +``` + +The `Bloc` class holds the current state and manages logic of states and notifies the list of listeners when states changes. +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. +```java +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +public class Bloc implements ListenerManager { +private State currentState; +private final List> listeners = new ArrayList<>(); + +public Bloc() { +this.currentState = new State(0); +} + +@Override +public void addListener(StateListener listener) { +listeners.add(listener); +listener.onStateChange(currentState); +} + +@Override +public void removeListener(StateListener listener) { +listeners.remove(listener); +} + +@Override +public List> getListeners() { +return Collections.unmodifiableList(listeners); +} + +private void emitState(State newState) { +currentState = newState; +for (StateListener listener : listeners) { +listener.onStateChange(currentState); +} +} + +public void increment() { +emitState(new State(currentState.getValue() + 1)); +} + +public void decrement() { +emitState(new State(currentState.getValue() - 1)); +} +} +``` +The `main` class have a simple gui to try and test the bloc pattern components separately from the ui components. +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 +```java +import javax.swing.*; +import java.awt.*; + +public class Main { +public static void main(String[] args) { +Bloc bloc = new Bloc(); +JFrame frame = new JFrame("Bloc Example"); +frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE); +frame.setSize(400, 300); +JLabel counterLabel = new JLabel("Counter: 0", SwingConstants.CENTER); +counterLabel.setFont(new Font("Arial", Font.BOLD, 20)); +JButton incrementButton = new JButton("Increment"); +JButton decrementButton = new JButton("Decrement"); +JButton toggleListenerButton = new JButton("Disable Listener"); + + frame.setLayout(new BorderLayout()); + frame.add(counterLabel, BorderLayout.CENTER); + frame.add(incrementButton, BorderLayout.NORTH); + frame.add(decrementButton, BorderLayout.SOUTH); + frame.add(toggleListenerButton, BorderLayout.EAST); + + StateListener stateListener = state -> counterLabel.setText("Counter: " + state.getValue()); + + bloc.addListener(stateListener); + + toggleListenerButton.addActionListener(e -> { + if (bloc.getListeners().contains(stateListener)) { + bloc.removeListener(stateListener); + toggleListenerButton.setText("Enable Listener"); + } else { + bloc.addListener(stateListener); + toggleListenerButton.setText("Disable Listener"); + } + }); + + incrementButton.addActionListener(e -> bloc.increment()); + decrementButton.addActionListener(e -> bloc.decrement()); + + frame.setVisible(true); +} +} +``` +## Program Output + +- **On Increment** + `Counter: 1` + +- **On Decrement** + `Counter: 0` + +- **Dynamic Listener Toggle** + - Listener disabled: Counter stops updating. + - Listener enabled: Counter updates again. + +--- + +## When to Use the Bloc Pattern + +Use the Bloc pattern when: + +- You need a centralized system to manage state updates. +- You want to dynamically add/remove listeners without tight coupling. +- You are building an event-driven or state-driven system, such as UI frameworks. +--- + +## Real-World Applications of Bloc Pattern + +- **UI State Management**: Reacting to button clicks, updating labels, and toggling views. +- **Event-driven Systems**: Handling multiple subscribers efficiently for state updates. +--- + +## Benefits and Trade-offs of Bloc Pattern + +### Benefits: +- Clean separation of state management and UI logic. +- Flexibility to dynamically add/remove listeners. +- Centralized state propagation. + +### Trade-offs: +- Adds some complexity with the listener management mechanism. +- May introduce performance concerns with excessive listeners. +- the bloc class handles too many methods which violates the single responsbility principle +--- + +## Related Patterns + +- **Observer**: Bloc is a specialized implementation of the Observer pattern. +- **Mediator**: Bloc centralizes communication and state propagation. +- **cubit**: bloC is more general implementation than cubit +--- + +## References and Credits + +- [Design Patterns: Elements of Reusable Object-Oriented Software](https://amzn.to/3w0pvKI) +- [Java Swing Documentation](https://docs.oracle.com/javase/tutorial/uiswing/) +- [Event-Driven Programming in Java](https://www.oracle.com/java/) +- [bloC archetecture](https://bloclibrary.dev/architecture/) +- [flutter bloC package](https://pub.dev/documentation/flutter_bloc/latest/) + From 2c042a3e69b77432f87211c42ebd7b3d088e2035 Mon Sep 17 00:00:00 2001 From: darkhyper24 Date: Sat, 7 Dec 2024 19:10:23 +0200 Subject: [PATCH 04/14] fixed checkstyle warnings --- bloC/Readme.md | 6 +- .../src/main/java/com/iluwatar/bloc/Bloc.java | 37 ++++++++++- .../com/iluwatar/bloc/ListenerManager.java | 25 +++++++- .../src/main/java/com/iluwatar/bloc/Main.java | 61 ++++++++++++++----- .../main/java/com/iluwatar/bloc/State.java | 28 ++++++--- .../java/com/iluwatar/bloc/StateListener.java | 15 ++++- 6 files changed, 141 insertions(+), 31 deletions(-) diff --git a/bloC/Readme.md b/bloC/Readme.md index aad696868589..01871fcfc4c8 100644 --- a/bloC/Readme.md +++ b/bloC/Readme.md @@ -54,6 +54,9 @@ The `State` class holds the representation of the state of the application that ```java package com.iluwatar.bloc; +import lombok.Getter; + +@Getter public class State { private final int value; @@ -61,9 +64,6 @@ public class State { this.value = value; } - public int getValue() { - return value; - } } ``` The `ListenerManager` interface manages the basic operations for the listeners and is implemented by bloc class diff --git a/bloC/src/main/java/com/iluwatar/bloc/Bloc.java b/bloC/src/main/java/com/iluwatar/bloc/Bloc.java index 154b9438538a..62b629fa734b 100644 --- a/bloC/src/main/java/com/iluwatar/bloc/Bloc.java +++ b/bloC/src/main/java/com/iluwatar/bloc/Bloc.java @@ -1,32 +1,62 @@ package com.iluwatar.bloc; + import java.util.ArrayList; import java.util.Collections; import java.util.List; +/** + * The Bloc class is responsible for managing the current state and notifying registered listeners + * whenever the state changes. It implements the ListenerManager interface, allowing listeners + * to be added, removed, and notified of state changes. + */ public class Bloc implements ListenerManager { + private State currentState; private final List> listeners = new ArrayList<>(); + /** + * Constructs a new Bloc instance with an initial state of value 0. + */ public Bloc() { this.currentState = new State(0); } + /** + * Adds a listener to receive state change notifications. + * + * @param listener the listener to add + */ @Override public void addListener(StateListener listener) { listeners.add(listener); listener.onStateChange(currentState); } + /** + * Removes a listener from receiving state change notifications. + * + * @param listener the listener to remove + */ @Override public void removeListener(StateListener listener) { listeners.remove(listener); } + /** + * Returns an unmodifiable list of all registered listeners. + * + * @return an unmodifiable list of listeners + */ @Override public List> getListeners() { return Collections.unmodifiableList(listeners); } + /** + * Emits a new state and notifies all registered listeners of the change. + * + * @param newState the new state to emit + */ private void emitState(State newState) { currentState = newState; for (StateListener listener : listeners) { @@ -34,12 +64,17 @@ private void emitState(State newState) { } } + /** + * Increments the current state value by 1 and notifies listeners of the change. + */ public void increment() { emitState(new State(currentState.getValue() + 1)); } + /** + * Decrements the current state value by 1 and notifies listeners of the change. + */ public void decrement() { emitState(new State(currentState.getValue() - 1)); } } - diff --git a/bloC/src/main/java/com/iluwatar/bloc/ListenerManager.java b/bloC/src/main/java/com/iluwatar/bloc/ListenerManager.java index 8643152237aa..66caeddafea0 100644 --- a/bloC/src/main/java/com/iluwatar/bloc/ListenerManager.java +++ b/bloC/src/main/java/com/iluwatar/bloc/ListenerManager.java @@ -1,9 +1,32 @@ package com.iluwatar.bloc; import java.util.List; +/** + * Interface for managing listeners for state changes. + * + * @param The type of state to be handled by the listeners. + */ + public interface ListenerManager { + + /** + * Adds a listener that will be notified of state changes. + * + * @param listener the listener to be added + */ void addListener(StateListener listener); + + /** + * Removes a listener so that it no longer receives state change notifications. + * + * @param listener the listener to be removed + */ void removeListener(StateListener listener); + + /** + * Returns a list of all listeners currently registered for state changes. + * + * @return a list of registered listeners + */ List> getListeners(); } - diff --git a/bloC/src/main/java/com/iluwatar/bloc/Main.java b/bloC/src/main/java/com/iluwatar/bloc/Main.java index dc6f28fece5c..40f89a0d0697 100644 --- a/bloC/src/main/java/com/iluwatar/bloc/Main.java +++ b/bloC/src/main/java/com/iluwatar/bloc/Main.java @@ -1,45 +1,74 @@ package com.iluwatar.bloc; -import javax.swing.*; -import java.awt.*; +import java.awt.BorderLayout; +import java.awt.Font; +import javax.swing.JButton; +import javax.swing.JFrame; +import javax.swing.JLabel; +import javax.swing.SwingConstants; +import javax.swing.WindowConstants; -public class Main -{ - public static void main(String[] args) - { - Bloc bloC = new Bloc(); + + + +/** + * The Main class demonstrates the use of the Bloc pattern in a simple GUI application. + * It creates a JFrame with buttons to increment, decrement, and toggle a listener + * that updates the counter value on the screen. + */ +public class Main { + + /** + * The entry point of the application. Initializes the GUI and sets up actions + * for the buttons and listener management. + * + * @param args command-line arguments (not used in this example) + */ + public static void main(String[] args) { + // Create a Bloc instance to manage the state + + + // Create and set up the JFrame JFrame frame = new JFrame("BloC example"); frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE); frame.setSize(400, 300); + + // Create a label to display the counter value JLabel counterLabel = new JLabel("Counter: 0", SwingConstants.CENTER); counterLabel.setFont(new Font("Arial", Font.BOLD, 20)); + + // Create buttons for increment, decrement, and toggling listener JButton incrementButton = new JButton("Increment"); JButton decrementButton = new JButton("Decrement"); JButton toggleListenerButton = new JButton("Disable Listener"); + + // Set layout and add components to the frame frame.setLayout(new BorderLayout()); frame.add(counterLabel, BorderLayout.CENTER); frame.add(incrementButton, BorderLayout.NORTH); frame.add(decrementButton, BorderLayout.SOUTH); frame.add(toggleListenerButton, BorderLayout.EAST); - + Bloc bloC = new Bloc(); + // Create a state listener to update the counter label when the state changes StateListener stateListener = state -> counterLabel.setText("Counter: " + state.getValue()); + // Add the listener to the Bloc instance bloC.addListener(stateListener); - toggleListenerButton.addActionListener(e -> - { - if (bloC.getListeners().contains(stateListener)) - { + // Set action listeners for buttons + toggleListenerButton.addActionListener(e -> { + if (bloC.getListeners().contains(stateListener)) { bloC.removeListener(stateListener); toggleListenerButton.setText("Enable Listener"); - } else - { + } else { bloC.addListener(stateListener); toggleListenerButton.setText("Disable Listener"); } - } - ); + }); + incrementButton.addActionListener(e -> bloC.increment()); decrementButton.addActionListener(e -> bloC.decrement()); + + // Make the frame visible frame.setVisible(true); } } diff --git a/bloC/src/main/java/com/iluwatar/bloc/State.java b/bloC/src/main/java/com/iluwatar/bloc/State.java index 63ca7b76368b..fcf13287bcf0 100644 --- a/bloC/src/main/java/com/iluwatar/bloc/State.java +++ b/bloC/src/main/java/com/iluwatar/bloc/State.java @@ -1,15 +1,27 @@ package com.iluwatar.bloc; -public class State -{ +import lombok.Getter; + +/** + * The {@code State} class represents a state with an integer value. + * This class encapsulates the value and provides methods to retrieve it. + */ +@Getter +public class State { + /** + * -- GETTER -- + * Returns the value of the state. + * + */ private final int value; - public State(int value) - { + /** + * Constructs a {@code State} with the specified value. + * + * @param value the value of the state + */ + public State(int value) { this.value = value; } - public int getValue() - { - return value; - } + } diff --git a/bloC/src/main/java/com/iluwatar/bloc/StateListener.java b/bloC/src/main/java/com/iluwatar/bloc/StateListener.java index 49cf411fc40e..49527441eedb 100644 --- a/bloC/src/main/java/com/iluwatar/bloc/StateListener.java +++ b/bloC/src/main/java/com/iluwatar/bloc/StateListener.java @@ -1,6 +1,17 @@ package com.iluwatar.bloc; -public interface StateListener -{ +/** + * The {@code StateListener} interface defines the contract for listening to state changes. + * Implementations of this interface should handle state changes and define actions to take when the state changes. + * + * @param the type of state that this listener will handle + */ +public interface StateListener { + + /** + * This method is called when the state has changed. + * + * @param state the updated state + */ void onStateChange(T state); } From ee39a5ce087e7e3d0d14d94f77a3e8178a0ddda9 Mon Sep 17 00:00:00 2001 From: darkhyper24 Date: Sat, 7 Dec 2024 20:19:40 +0200 Subject: [PATCH 05/14] added tests for the ui --- bloC/pom.xml | 19 +++- .../test/java/com/iluwatar/bloc/MainTest.java | 98 +++++++++++++++++++ 2 files changed, 114 insertions(+), 3 deletions(-) create mode 100644 bloC/src/test/java/com/iluwatar/bloc/MainTest.java diff --git a/bloC/pom.xml b/bloC/pom.xml index d5fd2c87ee69..930ae3c8415d 100644 --- a/bloC/pom.xml +++ b/bloC/pom.xml @@ -8,9 +8,7 @@ java-design-patterns 1.26.0-SNAPSHOT - bloC - 17 17 @@ -22,6 +20,21 @@ junit-jupiter-api test + + org.testng + testng + RELEASE + test + + + junit + junit + test + + + org.assertj + assertj-core + test + - \ No newline at end of file diff --git a/bloC/src/test/java/com/iluwatar/bloc/MainTest.java b/bloC/src/test/java/com/iluwatar/bloc/MainTest.java new file mode 100644 index 000000000000..1322d2ac9198 --- /dev/null +++ b/bloC/src/test/java/com/iluwatar/bloc/MainTest.java @@ -0,0 +1,98 @@ +package com.iluwatar.bloc; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; + +import javax.swing.*; +import java.awt.*; + +import static org.junit.Assert.assertEquals; + +public class MainTest { + + private JFrame frame; + private JLabel counterLabel; + private JButton incrementButton; + private JButton decrementButton; + private JButton toggleListenerButton; + private Bloc bloc; + private StateListener stateListener; + + @Before + public void setUp() { + bloc = new Bloc(); // Re-initialize the Bloc for each test + + frame = new JFrame("BloC example"); + frame.setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE); + frame.setSize(400, 300); + + counterLabel = new JLabel("Counter: 0", SwingConstants.CENTER); + counterLabel.setFont(new Font("Arial", Font.BOLD, 20)); + + incrementButton = new JButton("Increment"); + decrementButton = new JButton("Decrement"); + toggleListenerButton = new JButton("Disable Listener"); + + frame.setLayout(new BorderLayout()); + frame.add(counterLabel, BorderLayout.CENTER); + frame.add(incrementButton, BorderLayout.NORTH); + frame.add(decrementButton, BorderLayout.SOUTH); + frame.add(toggleListenerButton, BorderLayout.EAST); + + stateListener = state -> counterLabel.setText("Counter: " + state.getValue()); + bloc.addListener(stateListener); + + incrementButton.addActionListener(e -> bloc.increment()); + decrementButton.addActionListener(e -> bloc.decrement()); + toggleListenerButton.addActionListener(e -> { + if (bloc.getListeners().contains(stateListener)) { + bloc.removeListener(stateListener); + toggleListenerButton.setText("Enable Listener"); + } else { + bloc.addListener(stateListener); + toggleListenerButton.setText("Disable Listener"); + } + }); + + frame.setVisible(true); + } + + @After + public void tearDown() { + frame.dispose(); + bloc = new Bloc(); // Reset Bloc state after each test to avoid state carryover + } + + + @Test + public void testIncrementButton() { + simulateButtonClick(incrementButton); + assertEquals("Counter: 1", counterLabel.getText()); + } + + @Test + public void testDecrementButton() { + simulateButtonClick(decrementButton); + assertEquals("Counter: -1", counterLabel.getText()); + } + + @Test + public void testToggleListener() { + // Disable listener + simulateButtonClick(toggleListenerButton); + simulateButtonClick(incrementButton); + assertEquals("Counter: 0", counterLabel.getText()); // Listener is disabled + + // Enable listener + simulateButtonClick(toggleListenerButton); + simulateButtonClick(incrementButton); + assertEquals("Counter: 2", counterLabel.getText()); // Listener is re-enabled + } + + private void simulateButtonClick(JButton button) { + for (var listener : button.getActionListeners()) { + listener.actionPerformed(null); + } + } +} From 929eafb693a892cf3293c486c0197dc3a81224d6 Mon Sep 17 00:00:00 2001 From: darkhyper24 Date: Sat, 7 Dec 2024 21:15:04 +0200 Subject: [PATCH 06/14] fixed a test in MainTest file --- bloC/src/test/java/com/iluwatar/bloc/MainTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bloC/src/test/java/com/iluwatar/bloc/MainTest.java b/bloC/src/test/java/com/iluwatar/bloc/MainTest.java index 1322d2ac9198..ae38c7d67b1f 100644 --- a/bloC/src/test/java/com/iluwatar/bloc/MainTest.java +++ b/bloC/src/test/java/com/iluwatar/bloc/MainTest.java @@ -78,7 +78,7 @@ public void testDecrementButton() { } @Test - public void testToggleListener() { + public void testToggleListenerButton() { // Disable listener simulateButtonClick(toggleListenerButton); simulateButtonClick(incrementButton); From 9d33b16d3460f51e2cdbb7cb81f59dd9e1a0773b Mon Sep 17 00:00:00 2001 From: darkhyper24 Date: Mon, 16 Dec 2024 01:38:58 +0200 Subject: [PATCH 07/14] separating ui from main file and adding more tests --- bloC/pom.xml | 5 + .../main/java/com/iluwatar/bloc/BlocUi.java | 64 ++++++++++++ .../src/main/java/com/iluwatar/bloc/Main.java | 66 +------------ .../java/com/iluwatar/bloc/BlocUiTest.java | 98 +++++++++++++++++++ .../test/java/com/iluwatar/bloc/MainTest.java | 98 ++----------------- 5 files changed, 182 insertions(+), 149 deletions(-) create mode 100644 bloC/src/main/java/com/iluwatar/bloc/BlocUi.java create mode 100644 bloC/src/test/java/com/iluwatar/bloc/BlocUiTest.java diff --git a/bloC/pom.xml b/bloC/pom.xml index 930ae3c8415d..eda6a71d6640 100644 --- a/bloC/pom.xml +++ b/bloC/pom.xml @@ -36,5 +36,10 @@ assertj-core test + + org.mockito + mockito-inline + test + \ No newline at end of file diff --git a/bloC/src/main/java/com/iluwatar/bloc/BlocUi.java b/bloC/src/main/java/com/iluwatar/bloc/BlocUi.java new file mode 100644 index 000000000000..1cb0b0379de7 --- /dev/null +++ b/bloC/src/main/java/com/iluwatar/bloc/BlocUi.java @@ -0,0 +1,64 @@ +package com.iluwatar.bloc; + +import java.awt.BorderLayout; +import java.awt.Font; +import javax.swing.JButton; +import javax.swing.JFrame; +import javax.swing.JLabel; +import javax.swing.SwingConstants; +import javax.swing.WindowConstants; + +/** + * The BlocUI class handles the creation and management of the UI components. + */ +public class BlocUi { + + /** + * Creates and shows the UI. + */ + public void createAndShowUi() { + // Create a Bloc instance to manage the state + final Bloc bloc = new Bloc(); + + // setting up a frame window with a title + JFrame frame = new JFrame("BloC example"); + frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE); + frame.setSize(400, 300); + + // label to display the counter value + JLabel counterLabel = new JLabel("Counter: 0", SwingConstants.CENTER); + counterLabel.setFont(new Font("Arial", Font.BOLD, 20)); + + // buttons for increment, decrement, and toggling listener + JButton decrementButton = new JButton("Decrement"); + JButton toggleListenerButton = new JButton("Disable Listener"); + JButton incrementButton = new JButton("Increment"); + + frame.setLayout(new BorderLayout()); + frame.add(counterLabel, BorderLayout.CENTER); + frame.add(incrementButton, BorderLayout.NORTH); + frame.add(decrementButton, BorderLayout.SOUTH); + frame.add(toggleListenerButton, BorderLayout.EAST); + + // making a state listener to update the counter label when the state changes + StateListener stateListener = state -> counterLabel.setText("Counter: " + state.getValue()); + + // adding the listener to the Bloc instance + bloc.addListener(stateListener); + + toggleListenerButton.addActionListener(e -> { + if (bloc.getListeners().contains(stateListener)) { + bloc.removeListener(stateListener); + toggleListenerButton.setText("Enable Listener"); + } else { + bloc.addListener(stateListener); + toggleListenerButton.setText("Disable Listener"); + } + }); + + incrementButton.addActionListener(e -> bloc.increment()); + decrementButton.addActionListener(e -> bloc.decrement()); + + frame.setVisible(true); + } +} \ No newline at end of file diff --git a/bloC/src/main/java/com/iluwatar/bloc/Main.java b/bloC/src/main/java/com/iluwatar/bloc/Main.java index 40f89a0d0697..9751019c3c43 100644 --- a/bloC/src/main/java/com/iluwatar/bloc/Main.java +++ b/bloC/src/main/java/com/iluwatar/bloc/Main.java @@ -1,74 +1,18 @@ package com.iluwatar.bloc; -import java.awt.BorderLayout; -import java.awt.Font; -import javax.swing.JButton; -import javax.swing.JFrame; -import javax.swing.JLabel; -import javax.swing.SwingConstants; -import javax.swing.WindowConstants; - - - /** * The Main class demonstrates the use of the Bloc pattern in a simple GUI application. - * It creates a JFrame with buttons to increment, decrement, and toggle a listener - * that updates the counter value on the screen. + * It initializes the UI and sets up actions for the buttons and listener management. */ public class Main { /** - * The entry point of the application. Initializes the GUI and sets up actions - * for the buttons and listener management. + * The entry point of the application. Initializes the GUI. * * @param args command-line arguments (not used in this example) */ public static void main(String[] args) { - // Create a Bloc instance to manage the state - - - // Create and set up the JFrame - JFrame frame = new JFrame("BloC example"); - frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE); - frame.setSize(400, 300); - - // Create a label to display the counter value - JLabel counterLabel = new JLabel("Counter: 0", SwingConstants.CENTER); - counterLabel.setFont(new Font("Arial", Font.BOLD, 20)); - - // Create buttons for increment, decrement, and toggling listener - JButton incrementButton = new JButton("Increment"); - JButton decrementButton = new JButton("Decrement"); - JButton toggleListenerButton = new JButton("Disable Listener"); - - // Set layout and add components to the frame - frame.setLayout(new BorderLayout()); - frame.add(counterLabel, BorderLayout.CENTER); - frame.add(incrementButton, BorderLayout.NORTH); - frame.add(decrementButton, BorderLayout.SOUTH); - frame.add(toggleListenerButton, BorderLayout.EAST); - Bloc bloC = new Bloc(); - // Create a state listener to update the counter label when the state changes - StateListener stateListener = state -> counterLabel.setText("Counter: " + state.getValue()); - - // Add the listener to the Bloc instance - bloC.addListener(stateListener); - - // Set action listeners for buttons - toggleListenerButton.addActionListener(e -> { - if (bloC.getListeners().contains(stateListener)) { - bloC.removeListener(stateListener); - toggleListenerButton.setText("Enable Listener"); - } else { - bloC.addListener(stateListener); - toggleListenerButton.setText("Disable Listener"); - } - }); - - incrementButton.addActionListener(e -> bloC.increment()); - decrementButton.addActionListener(e -> bloC.decrement()); - - // Make the frame visible - frame.setVisible(true); + BlocUi blocUi = new BlocUi(); + blocUi.createAndShowUi(); } -} +} \ No newline at end of file diff --git a/bloC/src/test/java/com/iluwatar/bloc/BlocUiTest.java b/bloC/src/test/java/com/iluwatar/bloc/BlocUiTest.java new file mode 100644 index 000000000000..49917523cd93 --- /dev/null +++ b/bloC/src/test/java/com/iluwatar/bloc/BlocUiTest.java @@ -0,0 +1,98 @@ +package com.iluwatar.bloc; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; + +import javax.swing.*; +import java.awt.*; + +import static org.junit.Assert.assertEquals; + +public class BlocUiTest { + + private JFrame frame; + private JLabel counterLabel; + private JButton incrementButton; + private JButton decrementButton; + private JButton toggleListenerButton; + private Bloc bloc; + private StateListener stateListener; + + @Before + public void setUp() { + bloc = new Bloc(); // Re-initialize the Bloc for each test + + frame = new JFrame("BloC example"); + frame.setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE); + frame.setSize(400, 300); + + counterLabel = new JLabel("Counter: 0", SwingConstants.CENTER); + counterLabel.setFont(new Font("Arial", Font.BOLD, 20)); + + incrementButton = new JButton("Increment"); + decrementButton = new JButton("Decrement"); + toggleListenerButton = new JButton("Disable Listener"); + + frame.setLayout(new BorderLayout()); + frame.add(counterLabel, BorderLayout.CENTER); + frame.add(incrementButton, BorderLayout.NORTH); + frame.add(decrementButton, BorderLayout.SOUTH); + frame.add(toggleListenerButton, BorderLayout.EAST); + + stateListener = state -> counterLabel.setText("Counter: " + state.getValue()); + bloc.addListener(stateListener); + + incrementButton.addActionListener(e -> bloc.increment()); + decrementButton.addActionListener(e -> bloc.decrement()); + toggleListenerButton.addActionListener(e -> { + if (bloc.getListeners().contains(stateListener)) { + bloc.removeListener(stateListener); + toggleListenerButton.setText("Enable Listener"); + } else { + bloc.addListener(stateListener); + toggleListenerButton.setText("Disable Listener"); + } + }); + + frame.setVisible(true); + } + + @After + public void tearDown() { + frame.dispose(); + bloc = new Bloc(); // Reset Bloc state after each test to avoid state carryover + } + + + @Test + public void testIncrementButton() { + simulateButtonClick(incrementButton); + assertEquals("Counter: 1", counterLabel.getText()); + } + + @Test + public void testDecrementButton() { + simulateButtonClick(decrementButton); + assertEquals("Counter: -1", counterLabel.getText()); + } + + @Test + public void testToggleListenerButton() { + // Disable listener + simulateButtonClick(toggleListenerButton); + simulateButtonClick(incrementButton); + assertEquals("Counter: 0", counterLabel.getText()); // Listener is disabled + + // Enable listener + simulateButtonClick(toggleListenerButton); + simulateButtonClick(incrementButton); + assertEquals("Counter: 2", counterLabel.getText()); // Listener is re-enabled + } + + private void simulateButtonClick(JButton button) { + for (var listener : button.getActionListeners()) { + listener.actionPerformed(null); + } + } +} diff --git a/bloC/src/test/java/com/iluwatar/bloc/MainTest.java b/bloC/src/test/java/com/iluwatar/bloc/MainTest.java index ae38c7d67b1f..b8d1b0c70ca4 100644 --- a/bloC/src/test/java/com/iluwatar/bloc/MainTest.java +++ b/bloC/src/test/java/com/iluwatar/bloc/MainTest.java @@ -1,98 +1,20 @@ package com.iluwatar.bloc; -import org.junit.After; -import org.junit.Before; -import org.junit.Test; +import org.junit.jupiter.api.Test; -import javax.swing.*; -import java.awt.*; +import static org.mockito.Mockito.mockStatic; -import static org.junit.Assert.assertEquals; -public class MainTest { - - private JFrame frame; - private JLabel counterLabel; - private JButton incrementButton; - private JButton decrementButton; - private JButton toggleListenerButton; - private Bloc bloc; - private StateListener stateListener; - - @Before - public void setUp() { - bloc = new Bloc(); // Re-initialize the Bloc for each test - - frame = new JFrame("BloC example"); - frame.setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE); - frame.setSize(400, 300); - - counterLabel = new JLabel("Counter: 0", SwingConstants.CENTER); - counterLabel.setFont(new Font("Arial", Font.BOLD, 20)); - - incrementButton = new JButton("Increment"); - decrementButton = new JButton("Decrement"); - toggleListenerButton = new JButton("Disable Listener"); - - frame.setLayout(new BorderLayout()); - frame.add(counterLabel, BorderLayout.CENTER); - frame.add(incrementButton, BorderLayout.NORTH); - frame.add(decrementButton, BorderLayout.SOUTH); - frame.add(toggleListenerButton, BorderLayout.EAST); - - stateListener = state -> counterLabel.setText("Counter: " + state.getValue()); - bloc.addListener(stateListener); - - incrementButton.addActionListener(e -> bloc.increment()); - decrementButton.addActionListener(e -> bloc.decrement()); - toggleListenerButton.addActionListener(e -> { - if (bloc.getListeners().contains(stateListener)) { - bloc.removeListener(stateListener); - toggleListenerButton.setText("Enable Listener"); - } else { - bloc.addListener(stateListener); - toggleListenerButton.setText("Disable Listener"); - } - }); - - frame.setVisible(true); - } - - @After - public void tearDown() { - frame.dispose(); - bloc = new Bloc(); // Reset Bloc state after each test to avoid state carryover - } - - - @Test - public void testIncrementButton() { - simulateButtonClick(incrementButton); - assertEquals("Counter: 1", counterLabel.getText()); - } +class MainTest { @Test - public void testDecrementButton() { - simulateButtonClick(decrementButton); - assertEquals("Counter: -1", counterLabel.getText()); - } - - @Test - public void testToggleListenerButton() { - // Disable listener - simulateButtonClick(toggleListenerButton); - simulateButtonClick(incrementButton); - assertEquals("Counter: 0", counterLabel.getText()); // Listener is disabled - - // Enable listener - simulateButtonClick(toggleListenerButton); - simulateButtonClick(incrementButton); - assertEquals("Counter: 2", counterLabel.getText()); // Listener is re-enabled - } + void testMain() { + try (var mockedBlocUi = mockStatic(BlocUi.class)) { + // Call the main method + Main.main(new String[]{}); - private void simulateButtonClick(JButton button) { - for (var listener : button.getActionListeners()) { - listener.actionPerformed(null); + // Verify that createAndShowUi was called + mockedBlocUi.verify(() -> new BlocUi().createAndShowUi()); } } -} +} \ No newline at end of file From e0f4cab42351ecf76cf3a8989b13b60a9fdbd9f4 Mon Sep 17 00:00:00 2001 From: darkhyper24 Date: Tue, 14 Jan 2025 18:57:54 +0200 Subject: [PATCH 08/14] added pom.xml plugins and properties and fixed readme.md --- bloC/Readme.md | 6 ------ bloC/pom.xml | 41 +++++++++++++++++++++++++++++------------ 2 files changed, 29 insertions(+), 18 deletions(-) diff --git a/bloC/Readme.md b/bloC/Readme.md index 01871fcfc4c8..228af1634052 100644 --- a/bloC/Readme.md +++ b/bloC/Readme.md @@ -39,12 +39,6 @@ The Bloc pattern manages the state of an object and allows for dynamically notif ## Programmatic Example of the Bloc Pattern in Java -### 1. `Troll` Analogy Using Bloc - -Imagine a simple state-driven system that maintains a counter. Whenever the counter changes, any registered listener receives updates dynamically. - ---- - ### **Core Components of the Bloc Pattern** #### **1. State Object** diff --git a/bloC/pom.xml b/bloC/pom.xml index eda6a71d6640..08c629f6e36a 100644 --- a/bloC/pom.xml +++ b/bloC/pom.xml @@ -9,11 +9,6 @@ 1.26.0-SNAPSHOT bloC - - 17 - 17 - UTF-8 - org.junit.jupiter @@ -23,17 +18,12 @@ org.testng testng - RELEASE - test - - - junit - junit test org.assertj assertj-core + 3.24.2 test @@ -42,4 +32,31 @@ test - \ No newline at end of file + + + + org.apache.maven.plugins + maven-assembly-plugin + + + + com.iluwatar.bloC.App + + + + jar-with-dependencies + + + + + make-assembly + package + + single + + + + + + + From 6761a9f171a79a51109b4be8b6a7b8af844f1d2a Mon Sep 17 00:00:00 2001 From: darkhyper24 Date: Tue, 14 Jan 2025 19:42:38 +0200 Subject: [PATCH 09/14] fixed renaming problem and added context to main --- .../src/main/java/com/iluwatar/bloc/Main.java | 18 ----------- {bloC => bloc}/Readme.md | 0 bloC/etc/bloC.png => bloc/etc/bloc.png | Bin bloC/etc/bloC.puml => bloc/etc/bloc.puml | 0 {bloC => bloc}/pom.xml | 8 ++++- .../src/main/java/com/iluwatar/bloc/Bloc.java | 0 .../main/java/com/iluwatar/bloc/BlocUi.java | 0 .../com/iluwatar/bloc/ListenerManager.java | 0 .../src/main/java/com/iluwatar/bloc/Main.java | 28 ++++++++++++++++++ .../main/java/com/iluwatar/bloc/State.java | 0 .../java/com/iluwatar/bloc/StateListener.java | 0 .../test/java/com/iluwatar/bloc/BlocTest.java | 0 .../java/com/iluwatar/bloc/BlocUiTest.java | 0 .../test/java/com/iluwatar/bloc/MainTest.java | 0 pom.xml | 2 +- 15 files changed, 36 insertions(+), 20 deletions(-) delete mode 100644 bloC/src/main/java/com/iluwatar/bloc/Main.java rename {bloC => bloc}/Readme.md (100%) rename bloC/etc/bloC.png => bloc/etc/bloc.png (100%) rename bloC/etc/bloC.puml => bloc/etc/bloc.puml (100%) rename {bloC => bloc}/pom.xml (90%) rename {bloC => bloc}/src/main/java/com/iluwatar/bloc/Bloc.java (100%) rename {bloC => bloc}/src/main/java/com/iluwatar/bloc/BlocUi.java (100%) rename {bloC => bloc}/src/main/java/com/iluwatar/bloc/ListenerManager.java (100%) create mode 100644 bloc/src/main/java/com/iluwatar/bloc/Main.java rename {bloC => bloc}/src/main/java/com/iluwatar/bloc/State.java (100%) rename {bloC => bloc}/src/main/java/com/iluwatar/bloc/StateListener.java (100%) rename {bloC => bloc}/src/test/java/com/iluwatar/bloc/BlocTest.java (100%) rename {bloC => bloc}/src/test/java/com/iluwatar/bloc/BlocUiTest.java (100%) rename {bloC => bloc}/src/test/java/com/iluwatar/bloc/MainTest.java (100%) diff --git a/bloC/src/main/java/com/iluwatar/bloc/Main.java b/bloC/src/main/java/com/iluwatar/bloc/Main.java deleted file mode 100644 index 9751019c3c43..000000000000 --- a/bloC/src/main/java/com/iluwatar/bloc/Main.java +++ /dev/null @@ -1,18 +0,0 @@ -package com.iluwatar.bloc; - -/** - * The Main class demonstrates the use of the Bloc pattern in a simple GUI application. - * It initializes the UI and sets up actions for the buttons and listener management. - */ -public class Main { - - /** - * The entry point of the application. Initializes the GUI. - * - * @param args command-line arguments (not used in this example) - */ - public static void main(String[] args) { - BlocUi blocUi = new BlocUi(); - blocUi.createAndShowUi(); - } -} \ No newline at end of file diff --git a/bloC/Readme.md b/bloc/Readme.md similarity index 100% rename from bloC/Readme.md rename to bloc/Readme.md diff --git a/bloC/etc/bloC.png b/bloc/etc/bloc.png similarity index 100% rename from bloC/etc/bloC.png rename to bloc/etc/bloc.png diff --git a/bloC/etc/bloC.puml b/bloc/etc/bloc.puml similarity index 100% rename from bloC/etc/bloC.puml rename to bloc/etc/bloc.puml diff --git a/bloC/pom.xml b/bloc/pom.xml similarity index 90% rename from bloC/pom.xml rename to bloc/pom.xml index 08c629f6e36a..1edc49f03721 100644 --- a/bloC/pom.xml +++ b/bloc/pom.xml @@ -8,7 +8,7 @@ java-design-patterns 1.26.0-SNAPSHOT - bloC + bloc org.junit.jupiter @@ -18,6 +18,7 @@ org.testng testng + 7.7.1 test @@ -31,6 +32,11 @@ mockito-inline test + + junit + junit + test + diff --git a/bloC/src/main/java/com/iluwatar/bloc/Bloc.java b/bloc/src/main/java/com/iluwatar/bloc/Bloc.java similarity index 100% rename from bloC/src/main/java/com/iluwatar/bloc/Bloc.java rename to bloc/src/main/java/com/iluwatar/bloc/Bloc.java diff --git a/bloC/src/main/java/com/iluwatar/bloc/BlocUi.java b/bloc/src/main/java/com/iluwatar/bloc/BlocUi.java similarity index 100% rename from bloC/src/main/java/com/iluwatar/bloc/BlocUi.java rename to bloc/src/main/java/com/iluwatar/bloc/BlocUi.java diff --git a/bloC/src/main/java/com/iluwatar/bloc/ListenerManager.java b/bloc/src/main/java/com/iluwatar/bloc/ListenerManager.java similarity index 100% rename from bloC/src/main/java/com/iluwatar/bloc/ListenerManager.java rename to bloc/src/main/java/com/iluwatar/bloc/ListenerManager.java diff --git a/bloc/src/main/java/com/iluwatar/bloc/Main.java b/bloc/src/main/java/com/iluwatar/bloc/Main.java new file mode 100644 index 000000000000..1499e6211e56 --- /dev/null +++ b/bloc/src/main/java/com/iluwatar/bloc/Main.java @@ -0,0 +1,28 @@ +package com.iluwatar.bloc; + +/** + * The BLoC (Business Logic Component) pattern is a software design pattern primarily used + * in Flutter applications. It facilitates the separation of business logic from UI code, + * making the application more modular, testable, and scalable. The BLoC pattern uses streams + * to manage the flow of data and state changes, allowing widgets to react to new states as + * they arrive. + * In the BLoC pattern, the application is divided into three key components: + * - Input streams: Represent user interactions or external events fed into the BLoC. + * - Business logic: Processes the input and determines the resulting state or actions. + * - Output streams: Emit the updated state for the UI to consume. + * The BLoC pattern is especially useful in reactive programming scenarios and aligns well with the declarative nature of Flutter. + * By using this pattern, developers can ensure a clear separation of concerns, enhance reusability, and maintain consistent state management throughout the application. + */ + +public class Main { + + /** + * The entry point of the application. Initializes the GUI. + * + * @param args command-line arguments (not used in this example) + */ + public static void main(String[] args) { + BlocUi blocUi = new BlocUi(); + blocUi.createAndShowUi(); + } +} \ No newline at end of file diff --git a/bloC/src/main/java/com/iluwatar/bloc/State.java b/bloc/src/main/java/com/iluwatar/bloc/State.java similarity index 100% rename from bloC/src/main/java/com/iluwatar/bloc/State.java rename to bloc/src/main/java/com/iluwatar/bloc/State.java diff --git a/bloC/src/main/java/com/iluwatar/bloc/StateListener.java b/bloc/src/main/java/com/iluwatar/bloc/StateListener.java similarity index 100% rename from bloC/src/main/java/com/iluwatar/bloc/StateListener.java rename to bloc/src/main/java/com/iluwatar/bloc/StateListener.java diff --git a/bloC/src/test/java/com/iluwatar/bloc/BlocTest.java b/bloc/src/test/java/com/iluwatar/bloc/BlocTest.java similarity index 100% rename from bloC/src/test/java/com/iluwatar/bloc/BlocTest.java rename to bloc/src/test/java/com/iluwatar/bloc/BlocTest.java diff --git a/bloC/src/test/java/com/iluwatar/bloc/BlocUiTest.java b/bloc/src/test/java/com/iluwatar/bloc/BlocUiTest.java similarity index 100% rename from bloC/src/test/java/com/iluwatar/bloc/BlocUiTest.java rename to bloc/src/test/java/com/iluwatar/bloc/BlocUiTest.java diff --git a/bloC/src/test/java/com/iluwatar/bloc/MainTest.java b/bloc/src/test/java/com/iluwatar/bloc/MainTest.java similarity index 100% rename from bloC/src/test/java/com/iluwatar/bloc/MainTest.java rename to bloc/src/test/java/com/iluwatar/bloc/MainTest.java diff --git a/pom.xml b/pom.xml index 4786d4230974..c20b275168b9 100644 --- a/pom.xml +++ b/pom.xml @@ -218,7 +218,7 @@ function-composition microservices-distributed-tracing microservices-idempotent-consumer - bloC + bloc From fb6132c446ee1922c1bfa52a6d41e1a0dc573bcc Mon Sep 17 00:00:00 2001 From: darkhyper24 Date: Tue, 14 Jan 2025 19:55:58 +0200 Subject: [PATCH 10/14] chsnged state class to record --- .../src/main/java/com/iluwatar/bloc/Bloc.java | 4 ++-- .../main/java/com/iluwatar/bloc/BlocUi.java | 2 +- .../main/java/com/iluwatar/bloc/State.java | 23 ++----------------- .../test/java/com/iluwatar/bloc/BlocTest.java | 8 +++---- .../java/com/iluwatar/bloc/BlocUiTest.java | 2 +- 5 files changed, 10 insertions(+), 29 deletions(-) diff --git a/bloc/src/main/java/com/iluwatar/bloc/Bloc.java b/bloc/src/main/java/com/iluwatar/bloc/Bloc.java index 62b629fa734b..caed7080ab1c 100644 --- a/bloc/src/main/java/com/iluwatar/bloc/Bloc.java +++ b/bloc/src/main/java/com/iluwatar/bloc/Bloc.java @@ -68,13 +68,13 @@ private void emitState(State newState) { * Increments the current state value by 1 and notifies listeners of the change. */ public void increment() { - emitState(new State(currentState.getValue() + 1)); + emitState(new State(currentState.value() + 1)); } /** * Decrements the current state value by 1 and notifies listeners of the change. */ public void decrement() { - emitState(new State(currentState.getValue() - 1)); + emitState(new State(currentState.value() - 1)); } } diff --git a/bloc/src/main/java/com/iluwatar/bloc/BlocUi.java b/bloc/src/main/java/com/iluwatar/bloc/BlocUi.java index 1cb0b0379de7..e74536c10c40 100644 --- a/bloc/src/main/java/com/iluwatar/bloc/BlocUi.java +++ b/bloc/src/main/java/com/iluwatar/bloc/BlocUi.java @@ -41,7 +41,7 @@ public void createAndShowUi() { frame.add(toggleListenerButton, BorderLayout.EAST); // making a state listener to update the counter label when the state changes - StateListener stateListener = state -> counterLabel.setText("Counter: " + state.getValue()); + StateListener stateListener = state -> counterLabel.setText("Counter: " + state.value()); // adding the listener to the Bloc instance bloc.addListener(stateListener); diff --git a/bloc/src/main/java/com/iluwatar/bloc/State.java b/bloc/src/main/java/com/iluwatar/bloc/State.java index fcf13287bcf0..5e184d2af20b 100644 --- a/bloc/src/main/java/com/iluwatar/bloc/State.java +++ b/bloc/src/main/java/com/iluwatar/bloc/State.java @@ -1,27 +1,8 @@ package com.iluwatar.bloc; -import lombok.Getter; - /** * The {@code State} class represents a state with an integer value. * This class encapsulates the value and provides methods to retrieve it. */ -@Getter -public class State { - /** - * -- GETTER -- - * Returns the value of the state. - * - */ - private final int value; - - /** - * Constructs a {@code State} with the specified value. - * - * @param value the value of the state - */ - public State(int value) { - this.value = value; - } - -} +public record State(int value) { +} \ No newline at end of file diff --git a/bloc/src/test/java/com/iluwatar/bloc/BlocTest.java b/bloc/src/test/java/com/iluwatar/bloc/BlocTest.java index d3ef70b63ad3..e47d24634a70 100644 --- a/bloc/src/test/java/com/iluwatar/bloc/BlocTest.java +++ b/bloc/src/test/java/com/iluwatar/bloc/BlocTest.java @@ -19,14 +19,14 @@ void initialState() { @Test void IncrementUpdateState() { - bloc.addListener(state -> stateValue.set(state.getValue())); + bloc.addListener(state -> stateValue.set(state.value())); bloc.increment(); assertEquals(1, stateValue.get(), "State should increment to 1"); } @Test void DecrementUpdateState() { - bloc.addListener(state -> stateValue.set(state.getValue())); + bloc.addListener(state -> stateValue.set(state.value())); bloc.decrement(); assertEquals(-1, stateValue.get(), "State should decrement to -1"); } @@ -47,8 +47,8 @@ void removingListener() { @Test void multipleListeners() { AtomicInteger secondValue = new AtomicInteger(); - bloc.addListener(state -> stateValue.set(state.getValue())); - bloc.addListener(state -> secondValue.set(state.getValue())); + bloc.addListener(state -> stateValue.set(state.value())); + bloc.addListener(state -> secondValue.set(state.value())); bloc.increment(); assertEquals(1, stateValue.get(), "First listener should receive state 1."); assertEquals(1, secondValue.get(), "Second listener should receive state 1."); diff --git a/bloc/src/test/java/com/iluwatar/bloc/BlocUiTest.java b/bloc/src/test/java/com/iluwatar/bloc/BlocUiTest.java index 49917523cd93..7b793f89461a 100644 --- a/bloc/src/test/java/com/iluwatar/bloc/BlocUiTest.java +++ b/bloc/src/test/java/com/iluwatar/bloc/BlocUiTest.java @@ -40,7 +40,7 @@ public void setUp() { frame.add(decrementButton, BorderLayout.SOUTH); frame.add(toggleListenerButton, BorderLayout.EAST); - stateListener = state -> counterLabel.setText("Counter: " + state.getValue()); + stateListener = state -> counterLabel.setText("Counter: " + state.value()); bloc.addListener(stateListener); incrementButton.addActionListener(e -> bloc.increment()); From 1330e91f2f173aa3a4d3c3fa73777e715904e8ca Mon Sep 17 00:00:00 2001 From: darkhyper24 Date: Tue, 14 Jan 2025 20:30:31 +0200 Subject: [PATCH 11/14] syncing changes for conflicts --- pom.xml | 1 + 1 file changed, 1 insertion(+) diff --git a/pom.xml b/pom.xml index 5cc512b7de94..5f0d579cf78f 100644 --- a/pom.xml +++ b/pom.xml @@ -223,6 +223,7 @@ templateview money table-inheritance + bloc From 2e1e34afbffbd379cb9ba0744d3c60e73bfc0517 Mon Sep 17 00:00:00 2001 From: darkhyper24 <132711528+darkhyper24@users.noreply.github.com> Date: Tue, 14 Jan 2025 21:04:01 +0200 Subject: [PATCH 12/14] Revert "fixed conflicts" --- bloc/Readme.md | 228 ------------------ bloc/etc/bloc.png | Bin 67095 -> 0 bytes bloc/etc/bloc.puml | 41 ---- bloc/pom.xml | 68 ------ .../src/main/java/com/iluwatar/bloc/Bloc.java | 80 ------ .../main/java/com/iluwatar/bloc/BlocUi.java | 64 ----- .../com/iluwatar/bloc/ListenerManager.java | 32 --- .../src/main/java/com/iluwatar/bloc/Main.java | 28 --- .../main/java/com/iluwatar/bloc/State.java | 8 - .../java/com/iluwatar/bloc/StateListener.java | 17 -- .../test/java/com/iluwatar/bloc/BlocTest.java | 56 ----- .../java/com/iluwatar/bloc/BlocUiTest.java | 98 -------- .../test/java/com/iluwatar/bloc/MainTest.java | 20 -- pom.xml | 1 - 14 files changed, 741 deletions(-) delete mode 100644 bloc/Readme.md delete mode 100644 bloc/etc/bloc.png delete mode 100644 bloc/etc/bloc.puml delete mode 100644 bloc/pom.xml delete mode 100644 bloc/src/main/java/com/iluwatar/bloc/Bloc.java delete mode 100644 bloc/src/main/java/com/iluwatar/bloc/BlocUi.java delete mode 100644 bloc/src/main/java/com/iluwatar/bloc/ListenerManager.java delete mode 100644 bloc/src/main/java/com/iluwatar/bloc/Main.java delete mode 100644 bloc/src/main/java/com/iluwatar/bloc/State.java delete mode 100644 bloc/src/main/java/com/iluwatar/bloc/StateListener.java delete mode 100644 bloc/src/test/java/com/iluwatar/bloc/BlocTest.java delete mode 100644 bloc/src/test/java/com/iluwatar/bloc/BlocUiTest.java delete mode 100644 bloc/src/test/java/com/iluwatar/bloc/MainTest.java diff --git a/bloc/Readme.md b/bloc/Readme.md deleted file mode 100644 index 228af1634052..000000000000 --- a/bloc/Readme.md +++ /dev/null @@ -1,228 +0,0 @@ ---- -title: "Bloc Pattern in Java: State Management Simplified" -shortTitle: Bloc -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." -category: Structural -language: en -tag: - - State Management - - Event-driven - - Listener Management - - Object Composition - - Dynamic Behavior ---- - -## Also known as - -* Event-driven State Management -* State Listener Pattern - -## Intent of the Bloc Pattern - -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. - -## Detailed explanation of the Bloc pattern with real-World examples - -### Real-world example - -> 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. - -### In plain words - -> The Bloc pattern manages a single state object and dynamically notifies registered listeners whenever the state changes. - -### Wikipedia says - -> 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. - ---- - -## Programmatic Example of the Bloc Pattern in Java - -### **Core Components of the Bloc Pattern** - -#### **1. State Object** - -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. - -```java -package com.iluwatar.bloc; - -import lombok.Getter; - -@Getter -public class State { - private final int value; - - public State(int value) { - this.value = value; - } - -} -``` -The `ListenerManager` interface manages the basic operations for the listeners and is implemented by bloc class -```java -import java.util.List; - -public interface ListenerManager { - void addListener(StateListener listener); - void removeListener(StateListener listener); - List> getListeners(); -} -``` -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. -```java -public interface StateListener { -void onStateChange(T state); -} -``` - -The `Bloc` class holds the current state and manages logic of states and notifies the list of listeners when states changes. -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. -```java -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; - -public class Bloc implements ListenerManager { -private State currentState; -private final List> listeners = new ArrayList<>(); - -public Bloc() { -this.currentState = new State(0); -} - -@Override -public void addListener(StateListener listener) { -listeners.add(listener); -listener.onStateChange(currentState); -} - -@Override -public void removeListener(StateListener listener) { -listeners.remove(listener); -} - -@Override -public List> getListeners() { -return Collections.unmodifiableList(listeners); -} - -private void emitState(State newState) { -currentState = newState; -for (StateListener listener : listeners) { -listener.onStateChange(currentState); -} -} - -public void increment() { -emitState(new State(currentState.getValue() + 1)); -} - -public void decrement() { -emitState(new State(currentState.getValue() - 1)); -} -} -``` -The `main` class have a simple gui to try and test the bloc pattern components separately from the ui components. -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 -```java -import javax.swing.*; -import java.awt.*; - -public class Main { -public static void main(String[] args) { -Bloc bloc = new Bloc(); -JFrame frame = new JFrame("Bloc Example"); -frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE); -frame.setSize(400, 300); -JLabel counterLabel = new JLabel("Counter: 0", SwingConstants.CENTER); -counterLabel.setFont(new Font("Arial", Font.BOLD, 20)); -JButton incrementButton = new JButton("Increment"); -JButton decrementButton = new JButton("Decrement"); -JButton toggleListenerButton = new JButton("Disable Listener"); - - frame.setLayout(new BorderLayout()); - frame.add(counterLabel, BorderLayout.CENTER); - frame.add(incrementButton, BorderLayout.NORTH); - frame.add(decrementButton, BorderLayout.SOUTH); - frame.add(toggleListenerButton, BorderLayout.EAST); - - StateListener stateListener = state -> counterLabel.setText("Counter: " + state.getValue()); - - bloc.addListener(stateListener); - - toggleListenerButton.addActionListener(e -> { - if (bloc.getListeners().contains(stateListener)) { - bloc.removeListener(stateListener); - toggleListenerButton.setText("Enable Listener"); - } else { - bloc.addListener(stateListener); - toggleListenerButton.setText("Disable Listener"); - } - }); - - incrementButton.addActionListener(e -> bloc.increment()); - decrementButton.addActionListener(e -> bloc.decrement()); - - frame.setVisible(true); -} -} -``` -## Program Output - -- **On Increment** - `Counter: 1` - -- **On Decrement** - `Counter: 0` - -- **Dynamic Listener Toggle** - - Listener disabled: Counter stops updating. - - Listener enabled: Counter updates again. - ---- - -## When to Use the Bloc Pattern - -Use the Bloc pattern when: - -- You need a centralized system to manage state updates. -- You want to dynamically add/remove listeners without tight coupling. -- You are building an event-driven or state-driven system, such as UI frameworks. ---- - -## Real-World Applications of Bloc Pattern - -- **UI State Management**: Reacting to button clicks, updating labels, and toggling views. -- **Event-driven Systems**: Handling multiple subscribers efficiently for state updates. ---- - -## Benefits and Trade-offs of Bloc Pattern - -### Benefits: -- Clean separation of state management and UI logic. -- Flexibility to dynamically add/remove listeners. -- Centralized state propagation. - -### Trade-offs: -- Adds some complexity with the listener management mechanism. -- May introduce performance concerns with excessive listeners. -- the bloc class handles too many methods which violates the single responsbility principle ---- - -## Related Patterns - -- **Observer**: Bloc is a specialized implementation of the Observer pattern. -- **Mediator**: Bloc centralizes communication and state propagation. -- **cubit**: bloC is more general implementation than cubit ---- - -## References and Credits - -- [Design Patterns: Elements of Reusable Object-Oriented Software](https://amzn.to/3w0pvKI) -- [Java Swing Documentation](https://docs.oracle.com/javase/tutorial/uiswing/) -- [Event-Driven Programming in Java](https://www.oracle.com/java/) -- [bloC archetecture](https://bloclibrary.dev/architecture/) -- [flutter bloC package](https://pub.dev/documentation/flutter_bloc/latest/) - diff --git a/bloc/etc/bloc.png b/bloc/etc/bloc.png deleted file mode 100644 index 60d6eb77c8fc5e48a8538ed9e613e29a2d3be34d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 67095 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 diff --git a/bloc/etc/bloc.puml b/bloc/etc/bloc.puml deleted file mode 100644 index 5991f533ae70..000000000000 --- a/bloc/etc/bloc.puml +++ /dev/null @@ -1,41 +0,0 @@ -@startuml -package com.iluwatar.bloc { - - class State { - - value : int - + State(value : int) - + getValue() : int - } - - interface StateListener { - + onStateChange(state : T) - } - - interface ListenerManager { - + addListener(listener : StateListener) - + removeListener(listener : StateListener) - + getListeners() : List> - } - - class BloC { - - currentState : State - - listeners : List> - + BloC() - + addListener(listener : StateListener) - + removeListener(listener : StateListener) - + getListeners() : List> - - emitState(newState : State) - + increment() - + decrement() - } - - class Main { - + main(args : String[]) - } - - ListenerManager <|.. BloC - StateListener <|.. BloC - BloC o-- State - BloC *-- StateListener -} -@enduml diff --git a/bloc/pom.xml b/bloc/pom.xml deleted file mode 100644 index 1edc49f03721..000000000000 --- a/bloc/pom.xml +++ /dev/null @@ -1,68 +0,0 @@ - - - 4.0.0 - - com.iluwatar - java-design-patterns - 1.26.0-SNAPSHOT - - bloc - - - org.junit.jupiter - junit-jupiter-api - test - - - org.testng - testng - 7.7.1 - test - - - org.assertj - assertj-core - 3.24.2 - test - - - org.mockito - mockito-inline - test - - - junit - junit - test - - - - - - org.apache.maven.plugins - maven-assembly-plugin - - - - com.iluwatar.bloC.App - - - - jar-with-dependencies - - - - - make-assembly - package - - single - - - - - - - diff --git a/bloc/src/main/java/com/iluwatar/bloc/Bloc.java b/bloc/src/main/java/com/iluwatar/bloc/Bloc.java deleted file mode 100644 index caed7080ab1c..000000000000 --- a/bloc/src/main/java/com/iluwatar/bloc/Bloc.java +++ /dev/null @@ -1,80 +0,0 @@ -package com.iluwatar.bloc; - -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; - -/** - * The Bloc class is responsible for managing the current state and notifying registered listeners - * whenever the state changes. It implements the ListenerManager interface, allowing listeners - * to be added, removed, and notified of state changes. - */ -public class Bloc implements ListenerManager { - - private State currentState; - private final List> listeners = new ArrayList<>(); - - /** - * Constructs a new Bloc instance with an initial state of value 0. - */ - public Bloc() { - this.currentState = new State(0); - } - - /** - * Adds a listener to receive state change notifications. - * - * @param listener the listener to add - */ - @Override - public void addListener(StateListener listener) { - listeners.add(listener); - listener.onStateChange(currentState); - } - - /** - * Removes a listener from receiving state change notifications. - * - * @param listener the listener to remove - */ - @Override - public void removeListener(StateListener listener) { - listeners.remove(listener); - } - - /** - * Returns an unmodifiable list of all registered listeners. - * - * @return an unmodifiable list of listeners - */ - @Override - public List> getListeners() { - return Collections.unmodifiableList(listeners); - } - - /** - * Emits a new state and notifies all registered listeners of the change. - * - * @param newState the new state to emit - */ - private void emitState(State newState) { - currentState = newState; - for (StateListener listener : listeners) { - listener.onStateChange(currentState); - } - } - - /** - * Increments the current state value by 1 and notifies listeners of the change. - */ - public void increment() { - emitState(new State(currentState.value() + 1)); - } - - /** - * Decrements the current state value by 1 and notifies listeners of the change. - */ - public void decrement() { - emitState(new State(currentState.value() - 1)); - } -} diff --git a/bloc/src/main/java/com/iluwatar/bloc/BlocUi.java b/bloc/src/main/java/com/iluwatar/bloc/BlocUi.java deleted file mode 100644 index e74536c10c40..000000000000 --- a/bloc/src/main/java/com/iluwatar/bloc/BlocUi.java +++ /dev/null @@ -1,64 +0,0 @@ -package com.iluwatar.bloc; - -import java.awt.BorderLayout; -import java.awt.Font; -import javax.swing.JButton; -import javax.swing.JFrame; -import javax.swing.JLabel; -import javax.swing.SwingConstants; -import javax.swing.WindowConstants; - -/** - * The BlocUI class handles the creation and management of the UI components. - */ -public class BlocUi { - - /** - * Creates and shows the UI. - */ - public void createAndShowUi() { - // Create a Bloc instance to manage the state - final Bloc bloc = new Bloc(); - - // setting up a frame window with a title - JFrame frame = new JFrame("BloC example"); - frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE); - frame.setSize(400, 300); - - // label to display the counter value - JLabel counterLabel = new JLabel("Counter: 0", SwingConstants.CENTER); - counterLabel.setFont(new Font("Arial", Font.BOLD, 20)); - - // buttons for increment, decrement, and toggling listener - JButton decrementButton = new JButton("Decrement"); - JButton toggleListenerButton = new JButton("Disable Listener"); - JButton incrementButton = new JButton("Increment"); - - frame.setLayout(new BorderLayout()); - frame.add(counterLabel, BorderLayout.CENTER); - frame.add(incrementButton, BorderLayout.NORTH); - frame.add(decrementButton, BorderLayout.SOUTH); - frame.add(toggleListenerButton, BorderLayout.EAST); - - // making a state listener to update the counter label when the state changes - StateListener stateListener = state -> counterLabel.setText("Counter: " + state.value()); - - // adding the listener to the Bloc instance - bloc.addListener(stateListener); - - toggleListenerButton.addActionListener(e -> { - if (bloc.getListeners().contains(stateListener)) { - bloc.removeListener(stateListener); - toggleListenerButton.setText("Enable Listener"); - } else { - bloc.addListener(stateListener); - toggleListenerButton.setText("Disable Listener"); - } - }); - - incrementButton.addActionListener(e -> bloc.increment()); - decrementButton.addActionListener(e -> bloc.decrement()); - - frame.setVisible(true); - } -} \ No newline at end of file diff --git a/bloc/src/main/java/com/iluwatar/bloc/ListenerManager.java b/bloc/src/main/java/com/iluwatar/bloc/ListenerManager.java deleted file mode 100644 index 66caeddafea0..000000000000 --- a/bloc/src/main/java/com/iluwatar/bloc/ListenerManager.java +++ /dev/null @@ -1,32 +0,0 @@ -package com.iluwatar.bloc; -import java.util.List; - -/** - * Interface for managing listeners for state changes. - * - * @param The type of state to be handled by the listeners. - */ - -public interface ListenerManager { - - /** - * Adds a listener that will be notified of state changes. - * - * @param listener the listener to be added - */ - void addListener(StateListener listener); - - /** - * Removes a listener so that it no longer receives state change notifications. - * - * @param listener the listener to be removed - */ - void removeListener(StateListener listener); - - /** - * Returns a list of all listeners currently registered for state changes. - * - * @return a list of registered listeners - */ - List> getListeners(); -} diff --git a/bloc/src/main/java/com/iluwatar/bloc/Main.java b/bloc/src/main/java/com/iluwatar/bloc/Main.java deleted file mode 100644 index 1499e6211e56..000000000000 --- a/bloc/src/main/java/com/iluwatar/bloc/Main.java +++ /dev/null @@ -1,28 +0,0 @@ -package com.iluwatar.bloc; - -/** - * The BLoC (Business Logic Component) pattern is a software design pattern primarily used - * in Flutter applications. It facilitates the separation of business logic from UI code, - * making the application more modular, testable, and scalable. The BLoC pattern uses streams - * to manage the flow of data and state changes, allowing widgets to react to new states as - * they arrive. - * In the BLoC pattern, the application is divided into three key components: - * - Input streams: Represent user interactions or external events fed into the BLoC. - * - Business logic: Processes the input and determines the resulting state or actions. - * - Output streams: Emit the updated state for the UI to consume. - * The BLoC pattern is especially useful in reactive programming scenarios and aligns well with the declarative nature of Flutter. - * By using this pattern, developers can ensure a clear separation of concerns, enhance reusability, and maintain consistent state management throughout the application. - */ - -public class Main { - - /** - * The entry point of the application. Initializes the GUI. - * - * @param args command-line arguments (not used in this example) - */ - public static void main(String[] args) { - BlocUi blocUi = new BlocUi(); - blocUi.createAndShowUi(); - } -} \ No newline at end of file diff --git a/bloc/src/main/java/com/iluwatar/bloc/State.java b/bloc/src/main/java/com/iluwatar/bloc/State.java deleted file mode 100644 index 5e184d2af20b..000000000000 --- a/bloc/src/main/java/com/iluwatar/bloc/State.java +++ /dev/null @@ -1,8 +0,0 @@ -package com.iluwatar.bloc; - -/** - * The {@code State} class represents a state with an integer value. - * This class encapsulates the value and provides methods to retrieve it. - */ -public record State(int value) { -} \ No newline at end of file diff --git a/bloc/src/main/java/com/iluwatar/bloc/StateListener.java b/bloc/src/main/java/com/iluwatar/bloc/StateListener.java deleted file mode 100644 index 49527441eedb..000000000000 --- a/bloc/src/main/java/com/iluwatar/bloc/StateListener.java +++ /dev/null @@ -1,17 +0,0 @@ -package com.iluwatar.bloc; - -/** - * The {@code StateListener} interface defines the contract for listening to state changes. - * Implementations of this interface should handle state changes and define actions to take when the state changes. - * - * @param the type of state that this listener will handle - */ -public interface StateListener { - - /** - * This method is called when the state has changed. - * - * @param state the updated state - */ - void onStateChange(T state); -} diff --git a/bloc/src/test/java/com/iluwatar/bloc/BlocTest.java b/bloc/src/test/java/com/iluwatar/bloc/BlocTest.java deleted file mode 100644 index e47d24634a70..000000000000 --- a/bloc/src/test/java/com/iluwatar/bloc/BlocTest.java +++ /dev/null @@ -1,56 +0,0 @@ -package com.iluwatar.bloc; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import java.util.concurrent.atomic.AtomicInteger; -import static org.junit.jupiter.api.Assertions.*; - -class BlocTest { - private Bloc bloc; - private AtomicInteger stateValue; - @BeforeEach - void setUp() { - bloc = new Bloc(); - stateValue = new AtomicInteger(0); - } - @Test - void initialState() { - assertTrue(bloc.getListeners().isEmpty(), "No listeners should be present initially."); - } - - @Test - void IncrementUpdateState() { - bloc.addListener(state -> stateValue.set(state.value())); - bloc.increment(); - assertEquals(1, stateValue.get(), "State should increment to 1"); - } - - @Test - void DecrementUpdateState() { - bloc.addListener(state -> stateValue.set(state.value())); - bloc.decrement(); - assertEquals(-1, stateValue.get(), "State should decrement to -1"); - } - - @Test - void addingListener() { - bloc.addListener(state -> {}); - assertEquals(1, bloc.getListeners().size(), "Listener count should be 1."); - } - - @Test - void removingListener() { - StateListener listener = state -> {}; - bloc.addListener(listener); - bloc.removeListener(listener); - assertTrue(bloc.getListeners().isEmpty(), "Listener count should be 0 after removal."); - } - @Test - void multipleListeners() { - AtomicInteger secondValue = new AtomicInteger(); - bloc.addListener(state -> stateValue.set(state.value())); - bloc.addListener(state -> secondValue.set(state.value())); - bloc.increment(); - assertEquals(1, stateValue.get(), "First listener should receive state 1."); - assertEquals(1, secondValue.get(), "Second listener should receive state 1."); - } -} diff --git a/bloc/src/test/java/com/iluwatar/bloc/BlocUiTest.java b/bloc/src/test/java/com/iluwatar/bloc/BlocUiTest.java deleted file mode 100644 index 7b793f89461a..000000000000 --- a/bloc/src/test/java/com/iluwatar/bloc/BlocUiTest.java +++ /dev/null @@ -1,98 +0,0 @@ -package com.iluwatar.bloc; - -import org.junit.After; -import org.junit.Before; -import org.junit.Test; - -import javax.swing.*; -import java.awt.*; - -import static org.junit.Assert.assertEquals; - -public class BlocUiTest { - - private JFrame frame; - private JLabel counterLabel; - private JButton incrementButton; - private JButton decrementButton; - private JButton toggleListenerButton; - private Bloc bloc; - private StateListener stateListener; - - @Before - public void setUp() { - bloc = new Bloc(); // Re-initialize the Bloc for each test - - frame = new JFrame("BloC example"); - frame.setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE); - frame.setSize(400, 300); - - counterLabel = new JLabel("Counter: 0", SwingConstants.CENTER); - counterLabel.setFont(new Font("Arial", Font.BOLD, 20)); - - incrementButton = new JButton("Increment"); - decrementButton = new JButton("Decrement"); - toggleListenerButton = new JButton("Disable Listener"); - - frame.setLayout(new BorderLayout()); - frame.add(counterLabel, BorderLayout.CENTER); - frame.add(incrementButton, BorderLayout.NORTH); - frame.add(decrementButton, BorderLayout.SOUTH); - frame.add(toggleListenerButton, BorderLayout.EAST); - - stateListener = state -> counterLabel.setText("Counter: " + state.value()); - bloc.addListener(stateListener); - - incrementButton.addActionListener(e -> bloc.increment()); - decrementButton.addActionListener(e -> bloc.decrement()); - toggleListenerButton.addActionListener(e -> { - if (bloc.getListeners().contains(stateListener)) { - bloc.removeListener(stateListener); - toggleListenerButton.setText("Enable Listener"); - } else { - bloc.addListener(stateListener); - toggleListenerButton.setText("Disable Listener"); - } - }); - - frame.setVisible(true); - } - - @After - public void tearDown() { - frame.dispose(); - bloc = new Bloc(); // Reset Bloc state after each test to avoid state carryover - } - - - @Test - public void testIncrementButton() { - simulateButtonClick(incrementButton); - assertEquals("Counter: 1", counterLabel.getText()); - } - - @Test - public void testDecrementButton() { - simulateButtonClick(decrementButton); - assertEquals("Counter: -1", counterLabel.getText()); - } - - @Test - public void testToggleListenerButton() { - // Disable listener - simulateButtonClick(toggleListenerButton); - simulateButtonClick(incrementButton); - assertEquals("Counter: 0", counterLabel.getText()); // Listener is disabled - - // Enable listener - simulateButtonClick(toggleListenerButton); - simulateButtonClick(incrementButton); - assertEquals("Counter: 2", counterLabel.getText()); // Listener is re-enabled - } - - private void simulateButtonClick(JButton button) { - for (var listener : button.getActionListeners()) { - listener.actionPerformed(null); - } - } -} diff --git a/bloc/src/test/java/com/iluwatar/bloc/MainTest.java b/bloc/src/test/java/com/iluwatar/bloc/MainTest.java deleted file mode 100644 index b8d1b0c70ca4..000000000000 --- a/bloc/src/test/java/com/iluwatar/bloc/MainTest.java +++ /dev/null @@ -1,20 +0,0 @@ -package com.iluwatar.bloc; - -import org.junit.jupiter.api.Test; - -import static org.mockito.Mockito.mockStatic; - - -class MainTest { - - @Test - void testMain() { - try (var mockedBlocUi = mockStatic(BlocUi.class)) { - // Call the main method - Main.main(new String[]{}); - - // Verify that createAndShowUi was called - mockedBlocUi.verify(() -> new BlocUi().createAndShowUi()); - } - } -} \ No newline at end of file diff --git a/pom.xml b/pom.xml index 5f0d579cf78f..5cc512b7de94 100644 --- a/pom.xml +++ b/pom.xml @@ -223,7 +223,6 @@ templateview money table-inheritance - bloc From 502221bef2a2116fed5bc955d5d0465c6a585e54 Mon Sep 17 00:00:00 2001 From: darkhyper24 Date: Tue, 14 Jan 2025 21:32:44 +0200 Subject: [PATCH 13/14] restored files --- bloc/Readme.md | 228 ++++++++++++++++++ bloc/etc/bloc.png | Bin 0 -> 67095 bytes bloc/etc/bloc.puml | 41 ++++ bloc/pom.xml | 68 ++++++ .../src/main/java/com/iluwatar/bloc/Bloc.java | 80 ++++++ .../main/java/com/iluwatar/bloc/BlocUi.java | 64 +++++ .../com/iluwatar/bloc/ListenerManager.java | 32 +++ .../src/main/java/com/iluwatar/bloc/Main.java | 28 +++ .../main/java/com/iluwatar/bloc/State.java | 8 + .../java/com/iluwatar/bloc/StateListener.java | 17 ++ .../test/java/com/iluwatar/bloc/BlocTest.java | 56 +++++ .../java/com/iluwatar/bloc/BlocUiTest.java | 98 ++++++++ .../test/java/com/iluwatar/bloc/MainTest.java | 20 ++ pom.xml | 1 + 14 files changed, 741 insertions(+) create mode 100644 bloc/Readme.md create mode 100644 bloc/etc/bloc.png create mode 100644 bloc/etc/bloc.puml create mode 100644 bloc/pom.xml create mode 100644 bloc/src/main/java/com/iluwatar/bloc/Bloc.java create mode 100644 bloc/src/main/java/com/iluwatar/bloc/BlocUi.java create mode 100644 bloc/src/main/java/com/iluwatar/bloc/ListenerManager.java create mode 100644 bloc/src/main/java/com/iluwatar/bloc/Main.java create mode 100644 bloc/src/main/java/com/iluwatar/bloc/State.java create mode 100644 bloc/src/main/java/com/iluwatar/bloc/StateListener.java create mode 100644 bloc/src/test/java/com/iluwatar/bloc/BlocTest.java create mode 100644 bloc/src/test/java/com/iluwatar/bloc/BlocUiTest.java create mode 100644 bloc/src/test/java/com/iluwatar/bloc/MainTest.java diff --git a/bloc/Readme.md b/bloc/Readme.md new file mode 100644 index 000000000000..228af1634052 --- /dev/null +++ b/bloc/Readme.md @@ -0,0 +1,228 @@ +--- +title: "Bloc Pattern in Java: State Management Simplified" +shortTitle: Bloc +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." +category: Structural +language: en +tag: + - State Management + - Event-driven + - Listener Management + - Object Composition + - Dynamic Behavior +--- + +## Also known as + +* Event-driven State Management +* State Listener Pattern + +## Intent of the Bloc Pattern + +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. + +## Detailed explanation of the Bloc pattern with real-World examples + +### Real-world example + +> 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. + +### In plain words + +> The Bloc pattern manages a single state object and dynamically notifies registered listeners whenever the state changes. + +### Wikipedia says + +> 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. + +--- + +## Programmatic Example of the Bloc Pattern in Java + +### **Core Components of the Bloc Pattern** + +#### **1. State Object** + +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. + +```java +package com.iluwatar.bloc; + +import lombok.Getter; + +@Getter +public class State { + private final int value; + + public State(int value) { + this.value = value; + } + +} +``` +The `ListenerManager` interface manages the basic operations for the listeners and is implemented by bloc class +```java +import java.util.List; + +public interface ListenerManager { + void addListener(StateListener listener); + void removeListener(StateListener listener); + List> getListeners(); +} +``` +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. +```java +public interface StateListener { +void onStateChange(T state); +} +``` + +The `Bloc` class holds the current state and manages logic of states and notifies the list of listeners when states changes. +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. +```java +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +public class Bloc implements ListenerManager { +private State currentState; +private final List> listeners = new ArrayList<>(); + +public Bloc() { +this.currentState = new State(0); +} + +@Override +public void addListener(StateListener listener) { +listeners.add(listener); +listener.onStateChange(currentState); +} + +@Override +public void removeListener(StateListener listener) { +listeners.remove(listener); +} + +@Override +public List> getListeners() { +return Collections.unmodifiableList(listeners); +} + +private void emitState(State newState) { +currentState = newState; +for (StateListener listener : listeners) { +listener.onStateChange(currentState); +} +} + +public void increment() { +emitState(new State(currentState.getValue() + 1)); +} + +public void decrement() { +emitState(new State(currentState.getValue() - 1)); +} +} +``` +The `main` class have a simple gui to try and test the bloc pattern components separately from the ui components. +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 +```java +import javax.swing.*; +import java.awt.*; + +public class Main { +public static void main(String[] args) { +Bloc bloc = new Bloc(); +JFrame frame = new JFrame("Bloc Example"); +frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE); +frame.setSize(400, 300); +JLabel counterLabel = new JLabel("Counter: 0", SwingConstants.CENTER); +counterLabel.setFont(new Font("Arial", Font.BOLD, 20)); +JButton incrementButton = new JButton("Increment"); +JButton decrementButton = new JButton("Decrement"); +JButton toggleListenerButton = new JButton("Disable Listener"); + + frame.setLayout(new BorderLayout()); + frame.add(counterLabel, BorderLayout.CENTER); + frame.add(incrementButton, BorderLayout.NORTH); + frame.add(decrementButton, BorderLayout.SOUTH); + frame.add(toggleListenerButton, BorderLayout.EAST); + + StateListener stateListener = state -> counterLabel.setText("Counter: " + state.getValue()); + + bloc.addListener(stateListener); + + toggleListenerButton.addActionListener(e -> { + if (bloc.getListeners().contains(stateListener)) { + bloc.removeListener(stateListener); + toggleListenerButton.setText("Enable Listener"); + } else { + bloc.addListener(stateListener); + toggleListenerButton.setText("Disable Listener"); + } + }); + + incrementButton.addActionListener(e -> bloc.increment()); + decrementButton.addActionListener(e -> bloc.decrement()); + + frame.setVisible(true); +} +} +``` +## Program Output + +- **On Increment** + `Counter: 1` + +- **On Decrement** + `Counter: 0` + +- **Dynamic Listener Toggle** + - Listener disabled: Counter stops updating. + - Listener enabled: Counter updates again. + +--- + +## When to Use the Bloc Pattern + +Use the Bloc pattern when: + +- You need a centralized system to manage state updates. +- You want to dynamically add/remove listeners without tight coupling. +- You are building an event-driven or state-driven system, such as UI frameworks. +--- + +## Real-World Applications of Bloc Pattern + +- **UI State Management**: Reacting to button clicks, updating labels, and toggling views. +- **Event-driven Systems**: Handling multiple subscribers efficiently for state updates. +--- + +## Benefits and Trade-offs of Bloc Pattern + +### Benefits: +- Clean separation of state management and UI logic. +- Flexibility to dynamically add/remove listeners. +- Centralized state propagation. + +### Trade-offs: +- Adds some complexity with the listener management mechanism. +- May introduce performance concerns with excessive listeners. +- the bloc class handles too many methods which violates the single responsbility principle +--- + +## Related Patterns + +- **Observer**: Bloc is a specialized implementation of the Observer pattern. +- **Mediator**: Bloc centralizes communication and state propagation. +- **cubit**: bloC is more general implementation than cubit +--- + +## References and Credits + +- [Design Patterns: Elements of Reusable Object-Oriented Software](https://amzn.to/3w0pvKI) +- [Java Swing Documentation](https://docs.oracle.com/javase/tutorial/uiswing/) +- [Event-Driven Programming in Java](https://www.oracle.com/java/) +- [bloC archetecture](https://bloclibrary.dev/architecture/) +- [flutter bloC package](https://pub.dev/documentation/flutter_bloc/latest/) + diff --git a/bloc/etc/bloc.png b/bloc/etc/bloc.png new file mode 100644 index 0000000000000000000000000000000000000000..60d6eb77c8fc5e48a8538ed9e613e29a2d3be34d GIT binary patch literal 67095 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 literal 0 HcmV?d00001 diff --git a/bloc/etc/bloc.puml b/bloc/etc/bloc.puml new file mode 100644 index 000000000000..5991f533ae70 --- /dev/null +++ b/bloc/etc/bloc.puml @@ -0,0 +1,41 @@ +@startuml +package com.iluwatar.bloc { + + class State { + - value : int + + State(value : int) + + getValue() : int + } + + interface StateListener { + + onStateChange(state : T) + } + + interface ListenerManager { + + addListener(listener : StateListener) + + removeListener(listener : StateListener) + + getListeners() : List> + } + + class BloC { + - currentState : State + - listeners : List> + + BloC() + + addListener(listener : StateListener) + + removeListener(listener : StateListener) + + getListeners() : List> + - emitState(newState : State) + + increment() + + decrement() + } + + class Main { + + main(args : String[]) + } + + ListenerManager <|.. BloC + StateListener <|.. BloC + BloC o-- State + BloC *-- StateListener +} +@enduml diff --git a/bloc/pom.xml b/bloc/pom.xml new file mode 100644 index 000000000000..1edc49f03721 --- /dev/null +++ b/bloc/pom.xml @@ -0,0 +1,68 @@ + + + 4.0.0 + + com.iluwatar + java-design-patterns + 1.26.0-SNAPSHOT + + bloc + + + org.junit.jupiter + junit-jupiter-api + test + + + org.testng + testng + 7.7.1 + test + + + org.assertj + assertj-core + 3.24.2 + test + + + org.mockito + mockito-inline + test + + + junit + junit + test + + + + + + org.apache.maven.plugins + maven-assembly-plugin + + + + com.iluwatar.bloC.App + + + + jar-with-dependencies + + + + + make-assembly + package + + single + + + + + + + diff --git a/bloc/src/main/java/com/iluwatar/bloc/Bloc.java b/bloc/src/main/java/com/iluwatar/bloc/Bloc.java new file mode 100644 index 000000000000..caed7080ab1c --- /dev/null +++ b/bloc/src/main/java/com/iluwatar/bloc/Bloc.java @@ -0,0 +1,80 @@ +package com.iluwatar.bloc; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +/** + * The Bloc class is responsible for managing the current state and notifying registered listeners + * whenever the state changes. It implements the ListenerManager interface, allowing listeners + * to be added, removed, and notified of state changes. + */ +public class Bloc implements ListenerManager { + + private State currentState; + private final List> listeners = new ArrayList<>(); + + /** + * Constructs a new Bloc instance with an initial state of value 0. + */ + public Bloc() { + this.currentState = new State(0); + } + + /** + * Adds a listener to receive state change notifications. + * + * @param listener the listener to add + */ + @Override + public void addListener(StateListener listener) { + listeners.add(listener); + listener.onStateChange(currentState); + } + + /** + * Removes a listener from receiving state change notifications. + * + * @param listener the listener to remove + */ + @Override + public void removeListener(StateListener listener) { + listeners.remove(listener); + } + + /** + * Returns an unmodifiable list of all registered listeners. + * + * @return an unmodifiable list of listeners + */ + @Override + public List> getListeners() { + return Collections.unmodifiableList(listeners); + } + + /** + * Emits a new state and notifies all registered listeners of the change. + * + * @param newState the new state to emit + */ + private void emitState(State newState) { + currentState = newState; + for (StateListener listener : listeners) { + listener.onStateChange(currentState); + } + } + + /** + * Increments the current state value by 1 and notifies listeners of the change. + */ + public void increment() { + emitState(new State(currentState.value() + 1)); + } + + /** + * Decrements the current state value by 1 and notifies listeners of the change. + */ + public void decrement() { + emitState(new State(currentState.value() - 1)); + } +} diff --git a/bloc/src/main/java/com/iluwatar/bloc/BlocUi.java b/bloc/src/main/java/com/iluwatar/bloc/BlocUi.java new file mode 100644 index 000000000000..e74536c10c40 --- /dev/null +++ b/bloc/src/main/java/com/iluwatar/bloc/BlocUi.java @@ -0,0 +1,64 @@ +package com.iluwatar.bloc; + +import java.awt.BorderLayout; +import java.awt.Font; +import javax.swing.JButton; +import javax.swing.JFrame; +import javax.swing.JLabel; +import javax.swing.SwingConstants; +import javax.swing.WindowConstants; + +/** + * The BlocUI class handles the creation and management of the UI components. + */ +public class BlocUi { + + /** + * Creates and shows the UI. + */ + public void createAndShowUi() { + // Create a Bloc instance to manage the state + final Bloc bloc = new Bloc(); + + // setting up a frame window with a title + JFrame frame = new JFrame("BloC example"); + frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE); + frame.setSize(400, 300); + + // label to display the counter value + JLabel counterLabel = new JLabel("Counter: 0", SwingConstants.CENTER); + counterLabel.setFont(new Font("Arial", Font.BOLD, 20)); + + // buttons for increment, decrement, and toggling listener + JButton decrementButton = new JButton("Decrement"); + JButton toggleListenerButton = new JButton("Disable Listener"); + JButton incrementButton = new JButton("Increment"); + + frame.setLayout(new BorderLayout()); + frame.add(counterLabel, BorderLayout.CENTER); + frame.add(incrementButton, BorderLayout.NORTH); + frame.add(decrementButton, BorderLayout.SOUTH); + frame.add(toggleListenerButton, BorderLayout.EAST); + + // making a state listener to update the counter label when the state changes + StateListener stateListener = state -> counterLabel.setText("Counter: " + state.value()); + + // adding the listener to the Bloc instance + bloc.addListener(stateListener); + + toggleListenerButton.addActionListener(e -> { + if (bloc.getListeners().contains(stateListener)) { + bloc.removeListener(stateListener); + toggleListenerButton.setText("Enable Listener"); + } else { + bloc.addListener(stateListener); + toggleListenerButton.setText("Disable Listener"); + } + }); + + incrementButton.addActionListener(e -> bloc.increment()); + decrementButton.addActionListener(e -> bloc.decrement()); + + frame.setVisible(true); + } +} \ No newline at end of file diff --git a/bloc/src/main/java/com/iluwatar/bloc/ListenerManager.java b/bloc/src/main/java/com/iluwatar/bloc/ListenerManager.java new file mode 100644 index 000000000000..66caeddafea0 --- /dev/null +++ b/bloc/src/main/java/com/iluwatar/bloc/ListenerManager.java @@ -0,0 +1,32 @@ +package com.iluwatar.bloc; +import java.util.List; + +/** + * Interface for managing listeners for state changes. + * + * @param The type of state to be handled by the listeners. + */ + +public interface ListenerManager { + + /** + * Adds a listener that will be notified of state changes. + * + * @param listener the listener to be added + */ + void addListener(StateListener listener); + + /** + * Removes a listener so that it no longer receives state change notifications. + * + * @param listener the listener to be removed + */ + void removeListener(StateListener listener); + + /** + * Returns a list of all listeners currently registered for state changes. + * + * @return a list of registered listeners + */ + List> getListeners(); +} diff --git a/bloc/src/main/java/com/iluwatar/bloc/Main.java b/bloc/src/main/java/com/iluwatar/bloc/Main.java new file mode 100644 index 000000000000..1499e6211e56 --- /dev/null +++ b/bloc/src/main/java/com/iluwatar/bloc/Main.java @@ -0,0 +1,28 @@ +package com.iluwatar.bloc; + +/** + * The BLoC (Business Logic Component) pattern is a software design pattern primarily used + * in Flutter applications. It facilitates the separation of business logic from UI code, + * making the application more modular, testable, and scalable. The BLoC pattern uses streams + * to manage the flow of data and state changes, allowing widgets to react to new states as + * they arrive. + * In the BLoC pattern, the application is divided into three key components: + * - Input streams: Represent user interactions or external events fed into the BLoC. + * - Business logic: Processes the input and determines the resulting state or actions. + * - Output streams: Emit the updated state for the UI to consume. + * The BLoC pattern is especially useful in reactive programming scenarios and aligns well with the declarative nature of Flutter. + * By using this pattern, developers can ensure a clear separation of concerns, enhance reusability, and maintain consistent state management throughout the application. + */ + +public class Main { + + /** + * The entry point of the application. Initializes the GUI. + * + * @param args command-line arguments (not used in this example) + */ + public static void main(String[] args) { + BlocUi blocUi = new BlocUi(); + blocUi.createAndShowUi(); + } +} \ No newline at end of file diff --git a/bloc/src/main/java/com/iluwatar/bloc/State.java b/bloc/src/main/java/com/iluwatar/bloc/State.java new file mode 100644 index 000000000000..5e184d2af20b --- /dev/null +++ b/bloc/src/main/java/com/iluwatar/bloc/State.java @@ -0,0 +1,8 @@ +package com.iluwatar.bloc; + +/** + * The {@code State} class represents a state with an integer value. + * This class encapsulates the value and provides methods to retrieve it. + */ +public record State(int value) { +} \ No newline at end of file diff --git a/bloc/src/main/java/com/iluwatar/bloc/StateListener.java b/bloc/src/main/java/com/iluwatar/bloc/StateListener.java new file mode 100644 index 000000000000..49527441eedb --- /dev/null +++ b/bloc/src/main/java/com/iluwatar/bloc/StateListener.java @@ -0,0 +1,17 @@ +package com.iluwatar.bloc; + +/** + * The {@code StateListener} interface defines the contract for listening to state changes. + * Implementations of this interface should handle state changes and define actions to take when the state changes. + * + * @param the type of state that this listener will handle + */ +public interface StateListener { + + /** + * This method is called when the state has changed. + * + * @param state the updated state + */ + void onStateChange(T state); +} diff --git a/bloc/src/test/java/com/iluwatar/bloc/BlocTest.java b/bloc/src/test/java/com/iluwatar/bloc/BlocTest.java new file mode 100644 index 000000000000..26821366401e --- /dev/null +++ b/bloc/src/test/java/com/iluwatar/bloc/BlocTest.java @@ -0,0 +1,56 @@ +package com.iluwatar.bloc; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import java.util.concurrent.atomic.AtomicInteger; +import static org.junit.jupiter.api.Assertions.*; + +class BlocTest { + private Bloc bloc; + private AtomicInteger stateValue; + @BeforeEach + void setUp() { + bloc = new Bloc(); + stateValue = new AtomicInteger(0); + } + @Test + void initialState() { + assertTrue(bloc.getListeners().isEmpty(), "No listeners should be present initially."); + } + + @Test + void IncrementUpdateState() { + bloc.addListener(state -> stateValue.set(state.value())); + bloc.increment(); + assertEquals(1, stateValue.get(), "State should increment to 1"); + } + + @Test + void DecrementUpdateState() { + bloc.addListener(state -> stateValue.set(state.value())); + bloc.decrement(); + assertEquals(-1, stateValue.get(), "State should decrement to -1"); + } + + @Test + void addingListener() { + bloc.addListener(state -> {}); + assertEquals(1, bloc.getListeners().size(), "Listener count should be 1."); + } + + @Test + void removingListener() { + StateListener listener = state -> {}; + bloc.addListener(listener); + bloc.removeListener(listener); + assertTrue(bloc.getListeners().isEmpty(), "Listener count should be 0 after removal."); + } + @Test + void multipleListeners() { + AtomicInteger secondValue = new AtomicInteger(); + bloc.addListener(state -> stateValue.set(state.value())); + bloc.addListener(state -> secondValue.set(state.value())); + bloc.increment(); + assertEquals(1, stateValue.get(), "First listener should receive state 1."); + assertEquals(1, secondValue.get(), "Second listener should receive state 1."); + } +} \ No newline at end of file diff --git a/bloc/src/test/java/com/iluwatar/bloc/BlocUiTest.java b/bloc/src/test/java/com/iluwatar/bloc/BlocUiTest.java new file mode 100644 index 000000000000..7b793f89461a --- /dev/null +++ b/bloc/src/test/java/com/iluwatar/bloc/BlocUiTest.java @@ -0,0 +1,98 @@ +package com.iluwatar.bloc; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; + +import javax.swing.*; +import java.awt.*; + +import static org.junit.Assert.assertEquals; + +public class BlocUiTest { + + private JFrame frame; + private JLabel counterLabel; + private JButton incrementButton; + private JButton decrementButton; + private JButton toggleListenerButton; + private Bloc bloc; + private StateListener stateListener; + + @Before + public void setUp() { + bloc = new Bloc(); // Re-initialize the Bloc for each test + + frame = new JFrame("BloC example"); + frame.setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE); + frame.setSize(400, 300); + + counterLabel = new JLabel("Counter: 0", SwingConstants.CENTER); + counterLabel.setFont(new Font("Arial", Font.BOLD, 20)); + + incrementButton = new JButton("Increment"); + decrementButton = new JButton("Decrement"); + toggleListenerButton = new JButton("Disable Listener"); + + frame.setLayout(new BorderLayout()); + frame.add(counterLabel, BorderLayout.CENTER); + frame.add(incrementButton, BorderLayout.NORTH); + frame.add(decrementButton, BorderLayout.SOUTH); + frame.add(toggleListenerButton, BorderLayout.EAST); + + stateListener = state -> counterLabel.setText("Counter: " + state.value()); + bloc.addListener(stateListener); + + incrementButton.addActionListener(e -> bloc.increment()); + decrementButton.addActionListener(e -> bloc.decrement()); + toggleListenerButton.addActionListener(e -> { + if (bloc.getListeners().contains(stateListener)) { + bloc.removeListener(stateListener); + toggleListenerButton.setText("Enable Listener"); + } else { + bloc.addListener(stateListener); + toggleListenerButton.setText("Disable Listener"); + } + }); + + frame.setVisible(true); + } + + @After + public void tearDown() { + frame.dispose(); + bloc = new Bloc(); // Reset Bloc state after each test to avoid state carryover + } + + + @Test + public void testIncrementButton() { + simulateButtonClick(incrementButton); + assertEquals("Counter: 1", counterLabel.getText()); + } + + @Test + public void testDecrementButton() { + simulateButtonClick(decrementButton); + assertEquals("Counter: -1", counterLabel.getText()); + } + + @Test + public void testToggleListenerButton() { + // Disable listener + simulateButtonClick(toggleListenerButton); + simulateButtonClick(incrementButton); + assertEquals("Counter: 0", counterLabel.getText()); // Listener is disabled + + // Enable listener + simulateButtonClick(toggleListenerButton); + simulateButtonClick(incrementButton); + assertEquals("Counter: 2", counterLabel.getText()); // Listener is re-enabled + } + + private void simulateButtonClick(JButton button) { + for (var listener : button.getActionListeners()) { + listener.actionPerformed(null); + } + } +} diff --git a/bloc/src/test/java/com/iluwatar/bloc/MainTest.java b/bloc/src/test/java/com/iluwatar/bloc/MainTest.java new file mode 100644 index 000000000000..b8d1b0c70ca4 --- /dev/null +++ b/bloc/src/test/java/com/iluwatar/bloc/MainTest.java @@ -0,0 +1,20 @@ +package com.iluwatar.bloc; + +import org.junit.jupiter.api.Test; + +import static org.mockito.Mockito.mockStatic; + + +class MainTest { + + @Test + void testMain() { + try (var mockedBlocUi = mockStatic(BlocUi.class)) { + // Call the main method + Main.main(new String[]{}); + + // Verify that createAndShowUi was called + mockedBlocUi.verify(() -> new BlocUi().createAndShowUi()); + } + } +} \ No newline at end of file diff --git a/pom.xml b/pom.xml index 5cc512b7de94..5f0d579cf78f 100644 --- a/pom.xml +++ b/pom.xml @@ -223,6 +223,7 @@ templateview money table-inheritance + bloc From b5f255132447c942070c7deac357647275384417 Mon Sep 17 00:00:00 2001 From: darkhyper24 Date: Wed, 15 Jan 2025 22:47:00 +0200 Subject: [PATCH 14/14] renamed readme file and abstracted pom file --- bloc/{Readme.md => README.md} | 0 bloc/pom.xml | 22 +++++++--------------- 2 files changed, 7 insertions(+), 15 deletions(-) rename bloc/{Readme.md => README.md} (100%) diff --git a/bloc/Readme.md b/bloc/README.md similarity index 100% rename from bloc/Readme.md rename to bloc/README.md diff --git a/bloc/pom.xml b/bloc/pom.xml index 1edc49f03721..eeaef8c8cae2 100644 --- a/bloc/pom.xml +++ b/bloc/pom.xml @@ -43,23 +43,15 @@ org.apache.maven.plugins maven-assembly-plugin - - - - com.iluwatar.bloC.App - - - - jar-with-dependencies - - - make-assembly - package - - single - + + + + com.iluwatar.bloc.Main + + +