[Pharo-project] Beginner question about "self" in block
henrik.s.johansen at veloxit.no
Fri Jan 27 03:19:49 EST 2012
On Jan 27, 2012, at 6:11 53AM, Ben Coman wrote:
> Stéphane Ducasse wrote:
>>> Hi, I have one begginer question. It is may be simple, but it very baffles me.
>>> I am reading Pharo by Example (great book btw, thanks!). I'm in chapter two where I'm creating Lights Out game. There is this simple code http://pastebin.com/eQregZ35. What baffles me is line 10. I assign "Block of code" to mouseAction variable of LOCell. In this Block, there is "self", that obviously refers to LOGame object in that time. But when is this Block actualy EVALUATED (when I click on Cell), "self" should be reffering to LOCell object, isn't it? If I inspect one LOCell, inspector shows that it has instance variable
>> Here is a draft of a next chapter on block :)
>> But I should finish it :)
>> But I want to add how block are implemented at the bye code level so it take times because not that many people are helping, so I have to learn first.
> Thanks Stef. A very enlightening read. Its has helped in an example I'll relate for other neophytes, and in case there are any traps or patterns I am missing.
> I have been struggling with how to implement a bidirectional relationship between two classes such that consistency is enforced. Take for instance the following classes...
> Object subclass: #Book instanceVariableNames: 'bookTitle library'
> Object subclass: #Library instanceVariableNames: 'libraryName books'
> I want both the 'books' and 'library' instvars to remain private - meaning that I don't want the default accessors providing direct access to either. Then a method like 'Library>>addBook: aBook' which can update its internal state modifying the 'books' collection cannot update the internal 'library' state of 'aBook' - without Book having a setter method to directly change the 'library' instvar - which I want to avoid having. Trying to resolve this led me into recursion hell with too much cross checking and guarding code.
> What I was wanting was a way to expose the private state of one object to another object in a controlled manner. So now I think this might be achieved like this...
> Library>>addBook: aBook
> aBook addToLibrary: self.
> Book>>addToLibrary: aLibrary
> aLibrary addBook: self withBackLink: [ :backlinkValue | library := backlinkValue ].
> Library>>addBook: aBook withBackLink: setBacklinkBlock
> books ifNil: [ books := OrderedCollection new ].
> books add: aBook.
> setBacklinkBlock value: self.
When forgoing accessors to keep state private, it's usually a good idea to avoid lazy initialization, since you cannot centralize the check anywhere:
Library >> Initialize
books := OrderedCollection new.
Another common solution, is use two "levels" of methods: (basic* usually put in a private category)
Book >> addToLibrary: aLibrary
self basicAddToLibrary: aLibrary.
aLibrary basicAddBook: self.
Library >> addBook: aBook
self basicAddBook: aBook.
aBook uncheckedAddToLibrary: self.
Book basicAddToLibrary: aLibrary
library := aLibrary.
Library >> basicAddBook: aBook
books add: aBook
Basically, split the methods ensuring consistency from those actually modifying state.
Note that basicAddToLibrary is in essence a "default accessor" in this case, but not intended for public consumption.
IMHO, clarity in smalltalk is usually better achieved by categorization, naming, and comments than creating a restrictive API.
More information about the Pharo-dev