On Jan 27, 2012, at 6:11 53AM, Ben Coman wrote:
> Stéphane Ducasse wrote:
>> Welcome
>>
>>
>>
>>> 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.
>>
>> Stef
>>
> 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.
Cheers,
Henry