0-1-Many Pattern

Maloney johnm at wdi.disney.com
Tue Sep 8 18:56:45 UTC 1998


I much prefer your third approach. It keeps the entire implementation
inside Element, where it belongs. I don't consider the nil check
or the isCollection check to be bad style; they reflect very clearly
the implementation choices for 0, 1, and many elements. Adding a comment
to subelementsDo: would make the entire pattern completely
understandable just by reading this one method.

	-- John



>It is often the case that you want to create an Element object which
>contains zero or more subelements.  The most straight forward way is to use
>an instance var 'subelements' that aways contains a Collection of some
>kind.  However, sometimes for space reasons it is compelling to use the
>following:
>
>Subelement Count       Value stored in var 'subelements'
>0                       nil
>1                       direct Element reference
>2+                      Collection of Elements
>
>To have a method such as Element>>#subelementsDo: work in a polymorphic
>fashion you might define:
>
>Object>>#do: aBlock
>   ^aBlock value: self
>
>UndefinedObject>>#do:: aBlock
>   ^self
>
>As mentioned in previous posts, to avoid diluting the protocol of #do: and
>hiding errors, a separate message should be sent.  The question is what
>would be best?  This especially a problem when you want to make use of the
>existing Collection enumeration protocol beyond simple #do:.
>
>Two solution come immediately to mind:
>
>1)
>Object>>#asCollection
>    ^{self}
>
>UndefinedObject>>#asCollection
>    ^#()
>
>Collection>>#asCollection
>    ^self
>
>Element>>#subelements
>    ^subelements asCollection
>
>Element>>#subelementsDo: aBlock
>    ^self subelements do: aBlock
>
>Element>>#detectSubelement: aBlock
>    ^self subelements detect: aBlock
>
>
>2)
>Object>>#apply: aBlock
>    ^aBlock value: subelements
>
>UndefinedObject>>#apply: aBlock
>    "no op"
>
>Collection>>#apply: aBlock
>    ^self do: aBlock
>
>Element>>#subelementsDo: aBlock
>    ^subelements apply: aBlock
>
>Element>>#detectSubelement: aBlock
>    subelements apply: [:each | (aBlock value: each) ifTrue: [^each]].
>    ^nil
>
>Though Solution 1 avoids requiring an Element to hold a Collection if it
>doesn't have to, it perpetually creates and disposes Arrays.  It does
>however have the advantage of implicitly working with the entire
>enumeration protocol (e.g. detecting).
>
>Solution 2 is both statically and dynamically space efficient but requires
>rebuilding of any enumeration protocol beyond #do:.
>
>Taking a third approach, the non polymorphic method would be:
>
>3)
>Element>>#subelementsDo: aBlock
>    subelements ifNil: [^nil].
>    ^subelements isCollection
>        ifTrue: [subelements do: aBlock]
>        ifFalse: [aBlock value: subelements]
>
>Element>>#detectSubelement: aBlock
>    self subelementsDo:
>        [:subelement | (aBlock value: subelement) ifTrue: [^subelement]].
>    ^nil
>
>
>Which technique is recommended?
>
>--Maurice
>
>---------------------------------------------------------------------------
>  Maurice Rabb    773.281.6003    Stono Technologies, LLC    Chicago, USA





More information about the Squeak-dev mailing list