I have a number of ProtoObject subclasses in different packages that add
behaviour to a Model subclass by forwarding failed messages using #doesNotUnderstand:. I have choosen to do it this way, because I cannot add the added behaviour by subclassing the model, which has other subclasses that also need the new behaviour, which requires the use of additional instance variables and therefore cannot be implemented using loose methods. The unexpected behaviour that I will describe below is seen when one ProtoObject wraps another one that then wraps the Model. This is an example package that I have created (example code of the is found below the package): --------Package starts--------- | package | package := Package name: 'ProtoFailure'. package paxVersion: 0; basicComment: ''. package classNames add: #BottomProtoObject; add: #ModelForProtoObjects; add: #TopProtoObject; yourself. package binaryGlobalNames: (Set new yourself). package globalAliases: (Set new yourself). package allResourceNames: (Set new yourself). package setPrerequisites: (IdentitySet new add: '..\Object Arts\Dolphin\Base\Dolphin'; yourself). package! "Class Definitions"! Model subclass: #ModelForProtoObjects instanceVariableNames: '' classVariableNames: '' poolDictionaries: '' classInstanceVariableNames: ''! ProtoObject subclass: #BottomProtoObject instanceVariableNames: 'model' classVariableNames: '' poolDictionaries: '' classInstanceVariableNames: ''! ProtoObject subclass: #TopProtoObject instanceVariableNames: 'model' classVariableNames: '' poolDictionaries: '' classInstanceVariableNames: ''! "Global Aliases"! "Loose Methods"! "End of package definition"! "Source Globals"! "Classes"! ModelForProtoObjects guid: (GUID fromString: '{305F4BE9-47BB-4E4C-8DDF-8F8F9968B4EE}')! ModelForProtoObjects comment: ''! !ModelForProtoObjects categoriesForClass!MVP-Models! ! !ModelForProtoObjects methodsFor! modelBehaviour Transcript show: 'ModelForProtoObjects'; cr! ! !ModelForProtoObjects categoriesFor: #modelBehaviour!public! ! BottomProtoObject guid: (GUID fromString: '{56C13393-722E-4A98-A4EF-CF2368608DBD}')! BottomProtoObject comment: ''! !BottomProtoObject categoriesForClass!System-Support! ! !BottomProtoObject methodsFor! bottomBehaviour Transcript show: 'BottomProtoObject'; cr! doesNotUnderstand: failedMessage ^ failedMessage forwardTo: model! model ^model! model: anObject model := anObject! ! !BottomProtoObject categoriesFor: #bottomBehaviour!public! ! !BottomProtoObject categoriesFor: #doesNotUnderstand:!public! ! !BottomProtoObject categoriesFor: #model!accessing!private! ! !BottomProtoObject categoriesFor: #model:!accessing!private! ! TopProtoObject guid: (GUID fromString: '{B15680F9-5017-4A17-905B-4A567E6B53B0}')! TopProtoObject comment: ''! !TopProtoObject categoriesForClass!System-Support! ! !TopProtoObject methodsFor! doesNotUnderstand: failedMessage ^ failedMessage forwardTo: model! model ^model! model: anObject model := anObject! topBehaviour Transcript show: 'TopProtoObject'; cr! ! !TopProtoObject categoriesFor: #doesNotUnderstand:!public! ! !TopProtoObject categoriesFor: #model!accessing!private! ! !TopProtoObject categoriesFor: #model:!accessing!private! ! !TopProtoObject categoriesFor: #topBehaviour!public! ! "Binary Globals"! "Resources"! --------Package ends--------- --------Example code starts-------- After installing the above package, try the following in a workspace: model := ModelForProtoObjects new. model modelBehaviour. "this prints text in the Transcript." bottom := BottomProtoObject new. bottom model: model. bottom modelBehaviour. "this works. The failed message is forwarded to the model and text is shown." bottom bottomBehaviour. "this also works. The message is performed by the BottomProtoObject and text is shown." top := TopProtoObject new. top model: bottom. "we now wrap the BottomProtoObject in order to add even more behaviour." top modelBehaviour. "this works. The failed message is forwarded to the BottomProtoObject which forwards it to the model and text is shown." top topBehaviour. "this also works. The message is performed by the TopProtoObject and text is shown." top bottomBehaviour. "this does _not_ work. By stepping through the code it is obvious that the failed message is correctly forwarded to the BottomProtoObject which then claims that it doesn't understand the message, despite the fact that the message is implemented there. It is therefore forwarded to the ModelForProtoObjects, which of course doesn't understand it." top model bottomBehaviour. "however this works." -----Example code ends---- I didn't expect this behaviour and it took some time to figure out what was happening. What I wonder is if this is a bug or if the ProtoObjects cannot be used this way (and in that case, why not?). Best regards, Mikael Svane |
Mikael,
I think your problem is that the proto objects do not understand #perform:withArguments:; if you steal Object's code for it, the troublesome case should start to work. Have a good one, Bill -- Wilhelm K. Schwab, Ph.D. [hidden email] |
Bill,
Thanks! I had previously copied #perform:withArguments: to ProtoObject, but forgotten to add it to the loose methods of one of the packages. When restarting Dolphin (I often don't save the image. I use packages instead) the method of course was gone, which resulted in the strange behaviour. Best regards, Mikael Svane "Bill Schwab" <[hidden email]> skrev i meddelandet news:bp1ali$1js2t8$[hidden email]... > Mikael, > > I think your problem is that the proto objects do not understand > #perform:withArguments:; if you steal Object's code for it, the troublesome > case should start to work. > > Have a good one, > > Bill > > -- > Wilhelm K. Schwab, Ph.D. > [hidden email] > > |
In reply to this post by Mikael Svane
"Mikael Svane" <[hidden email]> wrote in message
news:bp1752$1iquv1$[hidden email]... > I have a number of ProtoObject subclasses in different packages that add > behaviour to a Model subclass by forwarding failed messages using > #doesNotUnderstand:. I have choosen to do it this way, because I cannot add > the added behaviour by subclassing the model, which has other subclasses > that also need the new behaviour, which requires the use of additional > instance variables and therefore cannot be implemented using loose methods. Three options you would have here are: 1) "Reserve" all the instance variables in the base package. 2) Use indexed instance variables encapsulating access to them through accessor methods so that they appear no different to named instance variables accessed in that way. 3) Use properties,. i.e. #propertyAt:[put:] and friends. The properties are stored in a global dictionary by default, but you can make them much more efficient by providing your own PropertyManager held in an instance variable. [Actually it is easiest to implement a similar mechanism using an identity dictionary held in an instance variable with the extra variables keyed by symbolic name, because the property methods are not as well factored as they might be]. 4) Use the package pre-install script to add the instance variables referenced from your loose method, and the post-uninstall script to remove them. Of these I would favour (4), but this might cause you problems if any of the objects in question are stored using STB. If that is the case then (3) (with a private property manager) is a reasonable option. (2) has potential STB conversion issues as well, but is otherwise a good approach. (1) is suitable if the set of extra instance variables is likely to be relatively static. Regards Blair |
Free forum by Nabble | Edit this page |