Separate allocation and initialization: provide them as separate operations rather than as a joint operation. [Low-level Design Pattern]
Context
You’re creating an object based on an external data structure. Part of the job is to create your object, and part is to populate it with the information from the external structure.
Forces
- We want to allocate and initialize at the same time, as it can be easier to allocate just the right space if we know how big the initialization will be.
- The allocation process may change independently of the initialization.
- The external structure may change independently of the structure we’re creating.
Resolution
Split allocation from initialization. That is, don’t provide a constructor like this:
public Form(Data data){...} //BAD
Instead, split this into a constructor and a “setter” method:
public Form() {...} //GOOD
public setData(Data data) {...}
(You may include the original constructor as a convenience method. It should just call the “good” form.)
Discussion
While the desire to use initialization information to guide allocation is understandable, the other forces should not be ignored. They tend to show up later, after the first form has been committed to. Change in allocation policy: If you want to change how the form is created (e.g., from a different pool), coupling the initialization to the allocation will interfere with that change.
Change in external structure: The structure of the internal data structure shouldn’t be highly coupled to the external structure. (For example, in populating a form, you will find that you might want to change the look of the form without changing the way it’s populated from the external data structure.)
Examples
- Building a form. We were creating a form that was to be populated from CORBA structure. Later in development, it turned out that we might want to re-populate the existing form (perhaps from a refresh in the structure). The original design only lets you populate it once: during initialization. Further, the form’s appearance would change (fields would change size, borders would be added, etc.), but the “appearance” code was intermingled with the “population” code. Splitting the constructor helped isolate the concerns.
- NextStep. Their version of Objective-C (a Smalltalk-like extension to C) used the form [Class new: data]. By modifying it to become [[Class alloc] init: data], they could change the allocation policy (providing for allocation from separate pools), and they were better able to support remote objects.
- Java Beans. The Java Beans model provides components for Java. To be a bean, a class must provide a no-argument constructor, and be Serializable. When a bean is instantiated, it is created by that constructor, and then populated from its serialized form.
Related Patterns
- Parnas suggested the rule of having each module/routine have a “secret”, the design decision it encapsulates. This pattern pushes a design more in that direction.
[Written 4-29-98]