TITLE: patterns for instance variables AUTHOR: Kent Beck <70761.1216@compuserve.com>, 18 Sept 95 This is from a forthcoming Smalltalk Report column. I tried to write patterns for instance variables like the ones I did for temps and failed. This is the result. Kent ---- Instance Variables The temporary variables boiled down to a simple set of patterns. You can use a temp to: * Cache a value for performance * Hold a value of a side-effecting expression * Explain a complex expression * Collect results from a complex enumeration I discovered these uses by looking at every method in the system that uses temporary variables and classifying them. Pretty soon the first three classifications became clear. After a while I had to add a fourth. When I tried to do the same thing for instance variables all I got was a muddle. I came up with 9 uses. Where temps were clear, though, these 9 uses are not. You can classify one variable as two or three at once. I also invented three (mostly orthogonal) styles of usage of instance variables. Ward Cunningham and I tried to figure out why instance variables are so much harder to pin down than temps. I wasn't satisfied with our answer, but here it is. Temporary variables are tactical. They are created to resolve a set of constraints that only exist within the scope of a single method. Instance variables are often created to solve much bigger problems, problems that may span many objects. In the process of writing a handbook for software engineering, we've been much more successful at canonizing coding practice than design or analysis practice. The decision to create an instance variable goes back to design or even analysis. It shouldn't be surprising that the result isn't crystal clear. Styles Having successfully lowered your expectations, here are the three styles I've found so far: * Private * Public * Acquaintance Private- These are instance variables that are a simple part of an object. They are used almost exclusively by the object itself within its own methods. A good example is the Visual Smalltalk version of OrderedCollection. It has variables "startPosition", "stopPosition", and "contents". No object outside of the OrderedCollection has any need for the values of these variables. Public- These are instance variables that are more complex parts of an object. They are often made available to the outside world for further processing. Often, they hold objects that are complex in their own right. However, if the referring object didn't exist, the object referred to by the variable wouldn't need to exist. Panes in Visual Smalltalk have an instance variable "pen" which holds a Pen. If you want to draw on a Pane, you need its pen. You can often improve your design by shifting responsibility into an object and making some of its public instance variables private. Acquaintance- These are variables that are there for convenience, but don't imply the sort of ownership of a private or public instance variable. Stream's instance variable "collection" is an acquaintance. If you have an Array you need to stream over, you could send it along with every message to the Stream (nextPut:on:, nextFrom:). The protocol would be much uglier and you would have the chance of errors if you used different collections at different times. Thus, Streams get acquainted with one and only one collection. Uses Here are the nine uses I've found so far: * Parent * Child * Name * Properties * Map * Current state/strategy * Pluggable selector/block * Cache * Flag Parent- Sometimes an owned object needs to acquaint itself with its owner. The owner provides context for calculations. VisualWorks VisualPart has an instance variable "container" which points to the containing VisualComponent. You can improve your designs by passing more context into the owned object and eliminating parent variables. This allows one object to be "owned" by several others. Child- In tree structures, interior nodes need a variable to hold a collection of children. VisualWorks CompositePart has a variable "components" that contains an OrderedCollection of VisualComponents. Name- If everyone who refers to an object must use the same key to identify it, the object needs a variable (probably public) to hold the key. You wouldn't want two clients to access the same Account with different numbers. Sometimes you can improve a design by replacing name variables with a Map (see below) in the owning object. Properties- Every instance of a class has the same state. What happens when every instance needs different state? Visual Smalltalk Panes, for example, have a host of optional values that can be (but don't need to be) set. Such an object needs a variable to hold a Dictionary mapping names to values. Unlike a Map (see below), a Property Dictionary's values have nothing in common. You can often improve a design by figuring out what the invariant state is, or finding distinct clusters of properties that can form their own objects. Map- Objects hold all the state associated with them. When an object is added that needs to associate new information with an existing object, adding a variable to the existing object would clutter it up. For example, Visual Smalltalk's ObjectFiler and VisualWorks' BOSS associate file offsets with objects. It wouldn't make sense to add a "fileOffset" instance variable to Object. Instead, each ObjectFiler keeps an IdentityDictionary mapping objects to file offsets. Unlike Properties, Maps have homogenous values. Sometimes you can improve designs by moving state out of an object and into a Map, or vice versa. Current state/strategy- When you use the State Object or Strategy Object pattern, you need a place to put the current state or strategy. VisualWorks' UIBuilder has an instance variable "policy" that holds an object that will create user interface widgets. Flag- When you have simple variable behavior where an object can be in one of two states, the object needs a variable to hold a Boolean to signify the state. VisualWorks' ParagraphEditor has a flag called "textHasChanged". It is true if the text has been edited. You can improve you designs if you have lots of flags, or if a flag shows up in lots of methods, by introducing a State Object or Strategy (see above). Pluggable selector/block- Every instance of a class has the same behavior but different state. Sometimes you need a little variable in behavior, but not enough to warrant creating a whole new class. Objects with slightly variable behavior need an instance variable to hold either a Symbol to be performed or a Block to be evaluated. Cache- Sometimes an object returns the same answer over and over in response to a message. If computing the answer is expensive, you can improve the performance of the system by adding an instance variable to the object to hold the value of the expression. You will have to make sure the value is recomputed if the value of the expression ever changes, and you should only add a cache if a performance profile of the object running under realistic conditions shows that the expression is expensive.