One obvious solution to this problem is to not create all of these additional objects. However, the nature of the framework requires this since components must be processed dynamically during every view render to apply things like appropriate ids, binding paths, and evaluated expressions which will likely be different for every instance of that particular component as cloned from the UIF definition.
So, eliminating the new component instances is just really not an option.
However, there are large portions of these component definitions which could remain static and cached and not necessarily need to be cloned. We could implement something like this with a variation of the flyweight pattern. In this case, we consider components immutable once they are loaded into the UIF (which essentially they are now, though it's not programatically enforced in any way). Then, instead of doing a full clone of the object, create a "thin" clone which has a pointer to the underlying component, but also allows for component instance specific values to be configured. In theory this could be done using proxies. However, I don't believe it will be feasible to do this with dynamic proxies in Java since they require the object being proxied has an interface on it which can be used for all external interaction. In the UIF, this is not the case as components can just be plain-old java classes which I think is appropriate.
These leaves us with a couple of other proxying options:
- CGLIB - Dynamic proxies using extension
- Javassist - dynamic proxies using extension or direct bytecode manipulation
- Load or Build-Time Weaving using AspectJ
Out of the three of these options, I'm exploring the Javassist option. The idea would be that whenever we go to "clone" a component, we instead create a proxy that wraps the immutable component underneath of it and delegates calls where needed, but stores modifications locally when they occur.