EMF is a very powerful framework and with power comes…responsibility. You can achieve great things with a minimum of effort using EMF, but if something goes wrong, you can also spend hours trying to find out why. This blog post is part of a series on things you should do and things you should not do when using EMF. You can use the link to the series pilot to navigate to the start and the link below to navigate to the next blog once it is published.
EMF Dos #6: Design a clear containment structure
With EMF, you can design a containment tree to structure your model as a tree. The containment tree — more formally, it is actually a containment forest — is the tree formed by the container reference of each EObject. In fact, it is the spanning tree of all containment references on the underlying graph induced by the model. It is a tree (or forest) because every EObject has at most one container that can be retrieved with its eContainer() method.
The containment tree is often used to serialize the model into Resources such as XMLResources. Every EObject in the containment tree of a resource is considered part of the resource in general (there is the exception of cross-resource containment). This structure also influences the behavior of EMF deleting EObjects. Removing an EObject from the containment reference in its container is considered to be the same as deleting the EObject and its containment siblings because they are not reachable by the containment structure from the root (resource) any more. As a side effect, a resource will often fail to save if there are cross references (non-containment) to EObjects not contained in a resource of the same resource set. Also, the containment tree is often used to display the model in a tree view and to determine how an EObject is copied by EcoreUtil.copy() . In summary, the containment tree is the logical structure of your model describing how things consist of each other and how they can be broken down into their smallest parts.
A well-designed model should include a carefully designed containment structure. You should clearly define where an EObject can be contained and, ideally, define the containment tree following the application domain. For example, if your model describes a car, the EReference to components of the car is a good candidate for a containment reference in the car. Copying the car or saving the car to a resource will also copy or save its components. Furthermore, it is a good idea to avoid a containment design where a large number of EObjects is contained in the same EReference. Not only is it difficult to display, but the performance of operations on this containment list will be poor in general. There are of course ways to tweak performance of large lists of EObjects contained in one container (and I will provide hints in one of the future blogs of this series), but often large containment lists are the sign of a poor containment design.
In many cases, you can design a containment model such that the containment structure in actual usage will be well-balanced trees because the end-users will understand the structure based on their domain knowledge and maintain the tree only for the sake of a “clean structure” from an application domain point of view.
Stay tuned for more Dos and Don´ts in my next blog!