Skip to content

Commit d0c685c

Browse files
committed
Remove descriptor class
1 parent 40a3ea7 commit d0c685c

File tree

12 files changed

+244
-199
lines changed

12 files changed

+244
-199
lines changed

README.md

Lines changed: 11 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -31,13 +31,13 @@ As a real example of using this framework, see [TabShell](https://github.com/tec
3131

3232
MVVM4FX reimagines the `Model``View``ViewModel` pattern for JavaFX as a component-based, extensible platform designed
3333
around clarity, modularity, and the KISS principle. Each `Component` exists as a self-contained unit composed of a
34-
`View`, `ViewModel`, `Mediator`, `Descriptor`, optionally extended with `History`.
34+
`View`, `ViewModel`, `Mediator` optionally extended with `History`.
3535

3636
The framework enforces a strict separation between presentation, logic, and identity. The `View` defines the visual
37-
structure and behavior; the `ViewModel` encapsulates logic and state; the `Descriptor` holds the component’s
38-
technical identity; the `Component` is responsible for initialization and deinitialization, managing child components,
39-
and their composition, operating at the component level. The `ComponentMediator` is the interface through which the
40-
`ViewModel` interacts with the `Component`, and the `History` preserves continuity across sessions.
37+
structure and behavior; the `ViewModel` encapsulates logic and state; the `Component` is responsible for initialization
38+
and deinitialization, managing child components, and their composition, operating at the component level. The
39+
`ComponentMediator` is the interface through which the `ViewModel` interacts with the `Component`, and the `History`
40+
preserves continuity across sessions.
4141

4242
At its core, MVVM4FX follows the KISS principle – every class, method, and abstraction exists only for a clear reason,
4343
avoiding unnecessary complexity or dependencies. This simplicity is deliberate: it keeps the architecture transparent,
@@ -238,19 +238,12 @@ MVVM and simplifies testing.
238238
* MVVM Compliance. The `Mediator` interface defines how a `ViewModel` can initiate the addition or removal of a
239239
component without violating MVVM principles.
240240

241-
In addition to the four classes, a component always has a `ComponentDescriptor` (which is provided by the framework
242-
and normally does not require custom implementation) and may include a `ComponentHistory`.
243-
244-
The `ComponentDescriptor` represents the internal metadata and platform-level state of a component. The descriptor
245-
acts as a technical identity card, containing all framework-related information while keeping it completely separate
246-
from business data. In other words, the purpose of this class is to ensure that internal component data does not mix
247-
with business data within the `ViewModel`.
248-
249-
The `ComponentHistory` enables the preservation of the component’s state across its lifecycle. Data exchange occurs
250-
exclusively between the `ComponentViewModel` and the `ComponentHistory`. When the component’s state transitions to
251-
`INITIALIZING`, data is restored from the `ComponentHistory` to the `ComponentViewModel`. Conversely, when the state
252-
transitions to `DEINITIALIZED`, data from the `ComponentViewModel` is saved back to the `ComponentHistory`. The volume
253-
of state information that is restored and persisted is defined by the `HistoryPolicy` enum.
241+
In addition to the four classes, a component may include a `ComponentHistory`. The `ComponentHistory` enables the
242+
preservation of the component’s state across its lifecycle. Data exchange occurs exclusively between the
243+
`ComponentViewModel` and the `ComponentHistory`. When the component’s state transitions to `INITIALIZING`, data is
244+
restored from the `ComponentHistory` to the `ComponentViewModel`. Conversely, when the state transitions to
245+
`DEINITIALIZED`, data from the `ComponentViewModel` is saved back to the `ComponentHistory`. The volume of state
246+
information that is restored and persisted is defined by the `HistoryPolicy` enum.
254247

255248
### Component Lifecycle<a name="component-lifecycle"></a>
256249

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

Lines changed: 185 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,11 @@
1616

1717
package com.techsenger.mvvm4fx.core;
1818

19+
import java.util.UUID;
20+
import javafx.beans.property.ObjectProperty;
21+
import javafx.beans.property.ReadOnlyObjectProperty;
22+
import javafx.beans.property.ReadOnlyObjectWrapper;
23+
import javafx.beans.property.SimpleObjectProperty;
1924
import org.slf4j.Logger;
2025
import org.slf4j.LoggerFactory;
2126

@@ -27,76 +32,233 @@ public abstract class AbstractComponent<T extends AbstractComponentView<?>> impl
2732

2833
private static final Logger logger = LoggerFactory.getLogger(AbstractComponent.class);
2934

35+
private static String logDelimiter = " :";
36+
37+
public static String getLogDelimiter() {
38+
return logDelimiter;
39+
}
40+
41+
public static void setLogDelimiter(String logDelimiter) {
42+
AbstractComponent.logDelimiter = logDelimiter;
43+
}
44+
3045
protected abstract class Mediator implements ComponentMediator {
3146

47+
@Override
48+
public ComponentName getName() {
49+
return AbstractComponent.this.getName();
50+
}
51+
52+
@Override
53+
public UUID getUuid() {
54+
return uuid;
55+
}
56+
57+
@Override
58+
public String getFullName() {
59+
return fullName;
60+
}
61+
62+
@Override
63+
public String getLogPrefix() {
64+
return logPrefix;
65+
}
66+
67+
@Override
68+
public ComponentState getState() {
69+
return state.get();
70+
}
71+
72+
@Override
73+
public ReadOnlyObjectProperty<ComponentState> stateProperty() {
74+
return state.getReadOnlyProperty();
75+
}
76+
77+
@Override
78+
public ObjectProperty<HistoryPolicy> historyPolicyProperty() {
79+
return historyPolicy;
80+
}
81+
82+
@Override
83+
public HistoryPolicy getHistoryPolicy() {
84+
return historyPolicy.get();
85+
}
86+
87+
@Override
88+
public void setHistoryPolicy(HistoryPolicy policy) {
89+
historyPolicy.set(policy);
90+
}
91+
92+
@Override
93+
public ComponentHistory<?> getHistory() {
94+
return history;
95+
}
96+
97+
@Override
98+
public ComponentGroup getGroup() {
99+
return group.get();
100+
}
101+
102+
@Override
103+
public void setGroup(ComponentGroup value) {
104+
group.set(value);
105+
}
106+
107+
@Override
108+
public ObjectProperty<ComponentGroup> groupProperty() {
109+
return group;
110+
}
32111
}
33112

34113
private final T view;
35114

115+
private final UUID uuid = UUID.randomUUID();
116+
117+
private final String fullName;
118+
119+
private final String logPrefix;
120+
121+
private final ReadOnlyObjectWrapper<ComponentState> state = new ReadOnlyObjectWrapper<>(ComponentState.CREATING);
122+
123+
private final ObjectProperty<HistoryPolicy> historyPolicy = new SimpleObjectProperty<>(HistoryPolicy.NONE);
124+
125+
private final ObjectProperty<ComponentGroup> group = new SimpleObjectProperty<>();
126+
127+
private HistoryProvider historyProvider;
128+
129+
private ComponentHistory<?> history;
130+
36131
public AbstractComponent(T view) {
37132
this.view = view;
38133
this.view.setComponent(this);
134+
long least32bits = uuid.getLeastSignificantBits() & 0xFFFFFFFFL;
135+
String shortUuid = String.format("%08X", least32bits);
136+
this.fullName = getName().getText() + "@" + shortUuid;
137+
this.logPrefix = resolveLogPrefix(fullName);
39138
}
40139

41140
@Override
42141
public T getView() {
43142
return view;
44143
}
45144

145+
@Override
146+
public UUID getUuid() {
147+
return uuid;
148+
}
149+
150+
@Override
151+
public String getFullName() {
152+
return fullName;
153+
}
154+
155+
@Override
156+
public String getLogPrefix() {
157+
return logPrefix;
158+
}
159+
160+
@Override
161+
public ComponentState getState() {
162+
return this.state.get();
163+
}
164+
165+
@Override
166+
public ReadOnlyObjectProperty<ComponentState> stateProperty() {
167+
return state.getReadOnlyProperty();
168+
}
169+
170+
@Override
171+
public ObjectProperty<HistoryPolicy> historyPolicyProperty() {
172+
return historyPolicy;
173+
}
174+
175+
@Override
176+
public HistoryPolicy getHistoryPolicy() {
177+
return historyPolicy.get();
178+
}
179+
180+
@Override
181+
public void setHistoryPolicy(HistoryPolicy policy) {
182+
historyPolicy.set(policy);
183+
}
184+
185+
@Override
186+
public ComponentHistory<?> getHistory() {
187+
return history;
188+
}
189+
190+
@Override
191+
public ComponentGroup getGroup() {
192+
return group.get();
193+
}
194+
195+
@Override
196+
public void setGroup(ComponentGroup value) {
197+
group.set(value);
198+
}
199+
200+
@Override
201+
public ObjectProperty<ComponentGroup> groupProperty() {
202+
return group;
203+
}
204+
46205
@Override
47206
public final void initialize() {
48207
var viewModel = this.view.getViewModel();
49-
var descriptor = viewModel.getDescriptor();
50208
try {
51-
var currentState = descriptor.getState();
52-
if (currentState != ComponentState.CREATING) {
53-
throw new IllegalStateException("Unexpected state of the component - " + currentState.name());
209+
if (getState() != ComponentState.CREATING) {
210+
throw new IllegalStateException("Unexpected state of the component - " + getState().name());
54211
}
55212
// pre-initialization
56213
preInitialize();
57214
// initialization
58-
descriptor.getStateWrapper().set(ComponentState.INITIALIZING);
215+
state.set(ComponentState.INITIALIZING);
59216
viewModel.initialize();
60217
this.view.initialize();
61-
descriptor.getStateWrapper().set(ComponentState.INITIALIZED);
62-
logger.debug("{} Initialized component", descriptor.getLogPrefix());
218+
state.set(ComponentState.INITIALIZED);
219+
logger.debug("{} Initialized component", logPrefix);
63220
// post-initialization
64221
postInitialize();
65222
} catch (Exception ex) {
66-
logger.error("{} Error initializing", descriptor.getLogPrefix(), ex);
223+
logger.error("{} Error initializing", logPrefix, ex);
67224
}
68225
}
69226

70227
@Override
71228
public final void deinitialize() {
72229
var viewModel = this.view.getViewModel();
73-
var descriptor = viewModel.getDescriptor();
74230
try {
75-
var currentState = descriptor.getState();
76-
if (currentState != ComponentState.INITIALIZED) {
77-
throw new IllegalStateException("Unexpected state of the component - " + currentState.name());
231+
if (getState() != ComponentState.INITIALIZED) {
232+
throw new IllegalStateException("Unexpected state of the component - " + getState().name());
78233
}
79234
// pre-deinitialization
80235
preDeinitialize();
81236
// deinitialization
82-
descriptor.getStateWrapper().set(ComponentState.DEINITIALIZING);
237+
state.set(ComponentState.DEINITIALIZING);
83238
this.view.deinitialize();
84239
viewModel.deinitialize();
85-
descriptor.getStateWrapper().set(ComponentState.DEINITIALIZED);
86-
logger.debug("{} Deinitialized component", descriptor.getLogPrefix());
240+
state.set(ComponentState.DEINITIALIZED);
241+
logger.debug("{} Deinitialized component", logPrefix);
87242
// post-deinitialization
88243
postDeinitialize();
89244
} catch (Exception ex) {
90-
logger.error("{} Error deinitializing", descriptor.getLogPrefix(), ex);
245+
logger.error("{} Error deinitializing", logPrefix, ex);
91246
}
92247
}
93248

249+
protected String resolveLogPrefix(String fullName) {
250+
return fullName + logDelimiter;
251+
}
252+
94253
/**
95254
* The first method called in initialization.
96255
*/
97256
protected void preInitialize() {
98257
var mediator = createMediator();
99258
this.view.getViewModel().setMediator(mediator);
259+
if (this.historyProvider != null) {
260+
this.history = this.historyProvider.provide();
261+
}
100262
this.view.getViewModel().restoreHistory();
101263
}
102264

@@ -114,7 +276,13 @@ protected void preDeinitialize() { }
114276
* The last method called in deinitialization.
115277
*/
116278
protected void postDeinitialize() {
117-
this.view.getViewModel().saveHistory();
279+
if (this.history != null) {
280+
this.view.getViewModel().saveHistory();
281+
}
282+
}
283+
284+
protected void setHistoryProvider(HistoryProvider historyProvider) {
285+
this.historyProvider = historyProvider;
118286
}
119287

120288
protected abstract AbstractComponent.Mediator createMediator();

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

Lines changed: 0 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -45,16 +45,6 @@ public AbstractComponent<?> getComponent() {
4545
return component;
4646
}
4747

48-
/**
49-
* Returns the component descriptor for convenient access. This is a shortcut method that delegates to the
50-
* underlying ViewModel's descriptor.
51-
*
52-
* @return the component descriptor
53-
*/
54-
protected ComponentDescriptor getDescriptor() {
55-
return this.viewModel.getDescriptor();
56-
}
57-
5848
/**
5949
* Performs initialization.
6050
*/

0 commit comments

Comments
 (0)