Skip to content

Commit 40c715a

Browse files
committed
Add composer and mediator methods
1 parent 4236c8b commit 40c715a

13 files changed

+73
-26
lines changed

README.md

Lines changed: 24 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ As a real example of using this framework, see [TabShell](https://github.com/tec
1717
* [Component Structure](#component-structure)
1818
* [Component Lifecycle](#component-lifecycle)
1919
* [Component Hierarchy](#component-hierarchy)
20-
* [Composite Component](#composite-component)
20+
* [Component Composer](#component-composer)
2121
* [When to Create a Component?](#when-to-create-component)
2222
* [When not to Create a Component?](#when-not-to-create-component)
2323
* [Requirements](#requirements)
@@ -144,8 +144,9 @@ exclusively between the `ComponentViewModel` and the `ComponentHistory`. When th
144144
transitions to `DEINITIALIZED`, data from the `ComponentViewModel` is saved back to the `ComponentHistory`. The volume
145145
of state information that is restored and persisted is defined by the `HistoryPolicy` enum.
146146

147-
The `ComponentComposer` is responsible for managing child components and their composition, while the `ComponentMediator`
148-
allows `ComponentViewModel` to interact with the `ComponentComposer` (see [Composite Component](#composite-component)).
147+
The `ComponentComposer` is responsible for managing child and derived components and their composition, while the
148+
`ComponentMediator` allows `ComponentViewModel` to interact with the `ComponentComposer`
149+
(see [Component Composer](#component-composer)).
149150

150151
### Component Lifecycle <a name="component-lifecycle"></a>
151152

@@ -194,15 +195,17 @@ between the presentation (`View`) and logic (`ViewModel`) layers. This design en
194195
across the component tree without violating the Unidirectional Hierarchy Rule (UHR), as the relationships are strictly
195196
hierarchical and non-cyclic.
196197

197-
### Composite Component <a name="composite-component"></a>
198+
### Component Composer<a name="component-composer"></a>
198199

199-
Components can be either simple or composite. A simple component has no child components. A composite component has
200-
one or more child components. The use of `Composer` and `Mediator` is required only for components that manage children
201-
or dynamically create other components, such as dialogs, panels, or complex containers. Working with a composite
202-
component is one of the most challenging parts of using the platform for the following reasons:
200+
The `ComponentComposer` is responsible for:
201+
1. Creating, positioning, and managing child components (those that will reside directly inside this component).
202+
2. Creating and positioning derived components (those that will be provided to another component after creation,
203+
e.g., dialogs, tabs, system notifications, etc.).
203204

204-
1. MVVM Gap. MVVM does not specify how child components should be created, how their lifecycle should be managed, or
205-
how they should be composed.
205+
Working with a composite component is one of the most challenging parts of using the platform for the following reasons:
206+
207+
1. MVVM Gap. MVVM does not specify how child and derived components should be created, how their lifecycle should be
208+
managed, or how they should be composed.
206209
2. Architectural Conflict. According to MVVM, the `ViewModel` must not know about the `View`, yet the `ViewModel` may
207210
need to initiate the creation of new components (for example, opening a dialog) and their composition — which is
208211
impossible without interacting with the `View`.
@@ -213,12 +216,11 @@ since names like `SomeComponentViewComposer` and `SomeComponentViewModelComposer
213216
must be created: `ChildView` extends `ParentView`, `ChildViewModel` extends `ParentViewModel`, `ChildComposer` extends
214217
`ParentComposer` etc.
215218

216-
In MVVM4FX, the solution for working with composite components is implemented using two classes: `Composer` and `Mediator`:
219+
In MVVM4FX, the solution is implemented using two classes: `ComponentComposer` and `ComponentMediator`:
217220

218-
`Mediator`. This is the interface that the `ViewModel` uses to interact with the `Composer`. The need for an
219-
interface is driven by two factors: first, it allows the `ViewModel` to be tested independently of other components;
220-
second, the `Composer` must know about both the `View` and the `ViewModel`, while the `ViewModel` must not know about
221-
the `View`.
221+
`Mediator`. This is the interface that the `ViewModel` uses to interact with the `Composer`. This interface is needed
222+
for two reasons: first, it allows the `ViewModel` to be tested independently; second, it allows the `Composer` to
223+
access both the `View` and the `ViewModel`, without exposing the `View` to the `ViewModel`.
222224

223225
```java
224226
public interface FooMediator extends ChildMediator {
@@ -237,7 +239,7 @@ public class FooViewModel extends AbstractChildViewModel {
237239
}
238240
```
239241

240-
`Composer`. This class contains the methods that manage the entire lifecycle of child components, as well as the
242+
`Composer`. This class contains the methods to work with child and derived components, as well as the
241243
methods the `View` uses to interact with the `Composer`. In addition, it defines a non-static inner class that
242244
implements the corresponding `Mediator`.
243245

@@ -248,6 +250,11 @@ public class FooComposer extends AbstractChildComposer<FooView> {
248250

249251
...
250252

253+
@Override
254+
public FooMediator getMediator() {
255+
return (FooMediator) super.Mediator();
256+
}
257+
251258
@Override
252259
protected FooMediator createMediator() {
253260
return new FooComposer.Mediator();
@@ -273,8 +280,7 @@ public class FooView extends AbstractChildView<FooViewModel> {
273280
Composer Creation and Initialization. The `Composer` is created during the component’s pre-initialization phase via
274281
the protected `AbstractComponentView#createComposer()` method. Creating the composer at this stage ensures that both
275282
the `View` and the `ViewModel` are fully constructed, allowing the composer to immediately access and interact with
276-
their properties and methods. The composer is also initialized during the pre-initialization phase, while its
277-
deinitialization takes place in the component’s post-deinitialization phase.
283+
their properties and methods. The composer is initialized before the `ViewModel` and deinitialized after the `ViewModel`.
278284

279285
Advantages of this approach:
280286

mvvm4fx-core/src/main/java/com/techsenger/mvvm4fx/core/AbstractChildComposer.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,4 +34,7 @@ public AbstractChildComposer(T view) {
3434
public ChildMediator getMediator() {
3535
return (ChildMediator) super.getMediator();
3636
}
37+
38+
@Override
39+
protected abstract ChildMediator createMediator();
3740
}

mvvm4fx-core/src/main/java/com/techsenger/mvvm4fx/core/AbstractChildView.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,11 @@ protected void addListeners(T viewModel) {
5959
});
6060
}
6161

62+
@Override
63+
protected ChildComposer<?> createComposer() {
64+
return (ChildComposer<?>) super.createComposer();
65+
}
66+
6267
/**
6368
* Sets the parent component for this component.
6469
* <p>

mvvm4fx-core/src/main/java/com/techsenger/mvvm4fx/core/AbstractComponentView.java

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,9 @@ public final void initialize() {
5252
}
5353
preInitialize(viewModel);
5454
descriptor.stateWrapper().set(ComponentState.INITIALIZING);
55+
if (this.composer != null) {
56+
this.composer.initialize();
57+
}
5558
viewModel.initialize();
5659
build(viewModel);
5760
bind(viewModel);
@@ -82,6 +85,9 @@ public final void deinitialize() {
8285
unbind(viewModel);
8386
unbuild(viewModel);
8487
viewModel.deinitialize();
88+
if (this.composer != null) {
89+
this.composer.deinitialize();
90+
}
8591
descriptor.stateWrapper().set(ComponentState.DEINITIALIZED);
8692
logger.debug("{} Deinitialized component", descriptor.getLogPrefix());
8793
postDeinitialize(viewModel);
@@ -90,6 +96,7 @@ public final void deinitialize() {
9096
}
9197
}
9298

99+
@Override
93100
public ComponentComposer<?> getComposer() {
94101
return composer;
95102
}
@@ -125,7 +132,6 @@ protected void preInitialize(T viewModel) {
125132
this.composer = createComposer();
126133
if (this.composer != null) {
127134
viewModel.setMediator(this.composer.getMediator());
128-
this.composer.initialize();
129135
}
130136
}
131137

@@ -210,8 +216,6 @@ protected void unbuild(T viewModel) {
210216
* The last method called in deinitialization.
211217
*/
212218
protected void postDeinitialize(T viewModel) {
213-
if (this.composer != null) {
214-
this.composer.deinitialize();
215-
}
219+
216220
}
217221
}

mvvm4fx-core/src/main/java/com/techsenger/mvvm4fx/core/AbstractComponentViewModel.java

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -109,14 +109,15 @@ public HistoryProvider getHistoryProvider() {
109109
return historyProvider;
110110
}
111111

112-
public void setHistoryProvider(HistoryProvider historyProvider) {
113-
this.historyProvider = historyProvider;
114-
}
115-
112+
@Override
116113
public ComponentMediator getMediator() {
117114
return this.mediator;
118115
}
119116

117+
public void setHistoryProvider(HistoryProvider historyProvider) {
118+
this.historyProvider = historyProvider;
119+
}
120+
120121
protected void initialize() {
121122

122123
}

mvvm4fx-core/src/main/java/com/techsenger/mvvm4fx/core/AbstractParentComposer.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,4 +34,7 @@ public AbstractParentComposer(T view) {
3434
public ParentMediator getMediator() {
3535
return (Mediator) super.getMediator();
3636
}
37+
38+
@Override
39+
protected abstract ParentMediator createMediator();
3740
}

mvvm4fx-core/src/main/java/com/techsenger/mvvm4fx/core/AbstractParentView.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,4 +87,9 @@ protected void addListeners(T viewModel) {
8787
}
8888
});
8989
}
90+
91+
@Override
92+
protected ParentComposer<?> createComposer() {
93+
return (ParentComposer<?>) super.createComposer();
94+
}
9095
}

mvvm4fx-core/src/main/java/com/techsenger/mvvm4fx/core/ChildView.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,4 +51,7 @@ public interface ChildView<T extends ChildViewModel> extends ParentView<T> {
5151
* @return
5252
*/
5353
Object getNode();
54+
55+
@Override
56+
ChildComposer<?> getComposer();
5457
}

mvvm4fx-core/src/main/java/com/techsenger/mvvm4fx/core/ChildViewModel.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,4 +39,6 @@ public interface ChildViewModel extends ParentViewModel {
3939
*/
4040
ParentViewModel getParent();
4141

42+
@Override
43+
ChildMediator getMediator();
4244
}

mvvm4fx-core/src/main/java/com/techsenger/mvvm4fx/core/ComponentView.java

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,4 +28,11 @@ public interface ComponentView<T extends ComponentViewModel> extends Lifecycle {
2828
* @return
2929
*/
3030
T getViewModel();
31+
32+
/**
33+
* Returns component composer.
34+
*
35+
* @return the composer or {@code null}
36+
*/
37+
ComponentComposer<?> getComposer();
3138
}

0 commit comments

Comments
 (0)