Graphical interfaces are a good example where variability is more convenient and more efficient to use as an object model. You create one support model for the GUI component with fields ready for read and write operations. The user mutates the display, which is reflected in the swap model - everything happens in one object. Imagine that you need to recreate an object every time a user changes something on one component? This will surely put you in order.
Despite the drawback, if you really want your GUI objects to be immutable, you can create a global event bus to solve the problem of attaching a listener. This way you do not need to worry about the instance of the object that the event listener will listen on. The event bus will be responsible for dispatching events, registering the listener, and maintaining consistency between them.
Here is a design for a simple event bus.
public class EventBus { private Map<Event, List<EventListener>> REGISTRY; public void registerEventListener(Event event, EventListener listener) { List<EventListener> listeners = REGISTRY.getOrDefault(event, new ArrayList<>()); listeners.add(listener); } public void fireEvent(Event event, Object... args) { List<EventListener> listeners = REGISTRY.get(event); if(listeners != null) { for(EventListener listener : listeners) { listener.handleEvent(args); } } } }
EDIT
Listeners are handlers — they must execute business logic and not preserve state. In addition, they should not be attached to the component. In the code above, the listener code must be separate from ImmutableWindow - both must stand alone. The interaction between ImmutableWindow and ImmutableButton must be configured (in the event bus) somewhere during the launch of your application.
You should also have a central registry of your user interface components, where it can be identified by a unique identifier and use this registry to search (cross the component tree) for the last instance of the component and interact with it.
In practice, something like this ...
You now have a completely decoupled interface and business logic. You can recreate your components, whatever you want, the listeners will not be affected, because they are not tied to any component.
Bnrdo source share