Pattern: Property


Context

Although it could easily be the result of ineffective class design, there are occasions when some attributes are not applicable to all instances of a class or the subclasses below it. If an instance variable is used to hold the attribute then this will waste space in all the instances for which the attribute has no relevance. There may be other mechanisms that can associate attribute information with an object without the need for an instance variable slot.

Solution

Dolphin provides a property mechanism for such eventualities. First, however, check that there isn't a better way to structure the classes to avoid the problem. A new subclass containing an instance variable for the additional attribute might be a more appropriate solution. Use the property mechanism only once you are satisfied that there is a genuine case that requires it.

Property accessing is provided for all objects by a number of methods in class Object. Look in the properties category to see the available methods. In particular, the following methods are most important:

	propertyAt: aSymbol
	propertyAt: aSymbol put: anObject
	removePropertyAt aSymbol:

Using the property mechanism instead of holding data in an instance variable is an implementation detail and should be hidden from clients of the class, and even the class itself. You should always provide accessor methods to get and set an attribute which has been implemented as a property.

Example

Imagine an architectural application. It contains a hierarchy of classes (Shape) representing the various elements in a drawing. Some attributes are common (indeed mandatory) to all of the classes, eg. size, position, these are instance variables. There may also be a label attribute which is common, but optional. This could be implemented as a property.

Shape>>label
	"Private - Answer the label belonging to the receiver,
	or nil if the receiver has no label associated with it."

	^self propertyAt: #label ifAbsent: []

Shape>>label: aString
	"Private - Set the label belonging to the receiver."

	^self propertyAt: #label put: aString 

Consequences

Using a property to hold attribute information for an object can save space in situations where the majority of instances of a class do not actually make use of the attribute. However, the standard property accessing mechanism is much slower than accessing an instance variable and the amount of memory required to store a property is greater.

Known Uses

The standard Smalltalk event system is implemented using the property mechanism.

Any object can register an interest in an event triggered by any other object. The triggering object must keep a list of interested parties. It could hold this collection as an instance variable, but this would add baggage (whether or not it was used) to every object in the system. Instead, the property mechanism is used.

Object>>when: anEventSymbol perform: aMessageSend
	"Adds aMessageSend to the event list for anEventSymbol in the receiver"

	| events |
	(events := self getEvents) isNil
		ifTrue: [events := EventsCollection new. self setEvents: events].
	events addEvent: anEventSymbol message: aMessageSend.

getEvents
	"Private - Answer the EventsCollection belonging to the receiver, or nil if the receiver
	has no events registered for it"

	^self propertyAt: #events ifAbsent: []

setEvents: anEventsCollectionOrNil
	"Private - Set the EventsCollection of the receiver to be anEventsCollectionOrNil.
	Answer the receiver."

	anEventsCollectionOrNil isNil
		ifTrue: [self removePropertyAt: #events ifAbsent: []]
		ifFalse: [self propertyAt: #events put: anEventsCollectionOrNil]
trigger: anEventSymbol
	"Evaluate the sequence of MessageSends registered for the receiver and the event, 
	anEventSymbol. Also signal a change for any dependents with the 
	trigger name as the aspect."

	self events triggerEvent: anEventSymbol.

Related Patterns