Smalltalk design advice - how to implement generic bi-directional pointers?

Previous Topic Next Topic
 
classic Classic list List threaded Threaded
29 messages Options
12
Reply | Threaded
Open this post in threaded view
|

Re: Re: Smalltalk design advice - how to implementgenericbi-directional pointers?

timrowledge

Assume you have a collection of all the projects that interest you;  
each project object (oh and be careful - there's a good chance that  
there is a class called Project already in existence, so choose a  
slightly more specific name) would likely instance variable for its  
name, accounting code (accountants love their code numbers), manager,  
purpose, company division, favourite pizza etc and, most importantly a  
collection of employees assigned to it.

In your project class, implement a method to query the collection of  
employees to find out if a particular employee is assigned. As it  
happens, the message #detect: already exists for this purpose; it  
works like this -
Given a collection containing fred, jim, bill, sheela, alice
  myCollection detect:[:element| element name = 'bill']
would return the first actual employee object which responded with  
true - so be careful with your tests since you may  give yourself  
false positives! Let us assume your test is adequately careful,  
perhaps because you actually test for identity rather than just a  
partial name; you will get back the appropriate employee object.

Unless it is not there - then you get an error. Whoops, better to use  
#detect:ifNode: in that case.  Like this
   myCollection detect:[employee| employee == theEmployeeIWant] ifNone:
[nil]
that way you either get the employee or nil.

So far then we can ask a particular project if a certain employee is  
working on it. What about finding all the project s/he is working on?  
There's a bunch of ways of iterating across collections and gathering  
results - take a look at the Collection class and in particular the  
'enumerating' protocol. In this case the #select: message is a  
reasonable choice since it builds a collection of all the elements  
that answer true when sent to the block -
  companyProjects select:[:proj| proj teamIncludes:  
theEmpoyeeOfInterest]

So - we have a nice encapsulated way of asking the core question and a  
nice way of building our list of projects the 'fred' works on. It's  
not at all hard to extend this to find all the projects involving  
several people, or a project that 'jim' isn't working on etc. There  
are lots of useful enumeration methods for youto make use of.

Please, please don't give in to the temptation to give the  
CompanyProject class a method that simply returns the collection of  
employees and then just use some awful C programmer crap to treat  
objects as mere arrays of data. Encapsulation will save you time,  
effort and sanity in the long run. Any time you see code looking like
things whotsits dooberries do:[db| db subelements do:[ ......
run, screaming, away.

tim
--
tim Rowledge; [hidden email]; http://www.rowledge.org/tim
Useful random insult:- Proof that evolution CAN go in reverse.


_______________________________________________
seaside mailing list
[hidden email]
http://lists.squeakfoundation.org/cgi-bin/mailman/listinfo/seaside
Reply | Threaded
Open this post in threaded view
|

Re: Re: Smalltalk design advice - how toimplementgenericbi-directional pointers?

Boris Popov, DeepCove Labs (SNN)
In reply to this post by Sebastian Sastre-2
Re: [Seaside] Re: Smalltalk design advice - how toimplementgenericbi-directional pointers?

Except favourite pizza is usually some kind of aggregate of team members preferences depending on their number, seniority and drunkenness.

Cheers!

-Boris (via BlackBerry)

----- Original Message -----
From: [hidden email] <[hidden email]>
To: Seaside - general discussion <[hidden email]>
Sent: Mon Dec 24 21:33:04 2007
Subject: Re: [Seaside] Re: Smalltalk design advice - how toimplementgenericbi-directional pointers?


Assume you have a collection of all the projects that interest you; 
each project object (oh and be careful - there's a good chance that 
there is a class called Project already in existence, so choose a 
slightly more specific name) would likely instance variable for its 
name, accounting code (accountants love their code numbers), manager, 
purpose, company division, favourite pizza etc and, most importantly a 
collection of employees assigned to it.

In your project class, implement a method to query the collection of 
employees to find out if a particular employee is assigned. As it 
happens, the message #detect: already exists for this purpose; it 
works like this -
Given a collection containing fred, jim, bill, sheela, alice
  myCollection detect:[:element| element name = 'bill']
would return the first actual employee object which responded with 
true - so be careful with your tests since you may  give yourself 
false positives! Let us assume your test is adequately careful, 
perhaps because you actually test for identity rather than just a 
partial name; you will get back the appropriate employee object.

Unless it is not there - then you get an error. Whoops, better to use 
#detect:ifNode: in that case.  Like this
   myCollection detect:[employee| employee == theEmployeeIWant] ifNone:
[nil]
that way you either get the employee or nil.

So far then we can ask a particular project if a certain employee is 
working on it. What about finding all the project s/he is working on? 
There's a bunch of ways of iterating across collections and gathering 
results - take a look at the Collection class and in particular the 
'enumerating' protocol. In this case the #select: message is a 
reasonable choice since it builds a collection of all the elements 
that answer true when sent to the block -
  companyProjects select:[:proj| proj teamIncludes: 
theEmpoyeeOfInterest]

So - we have a nice encapsulated way of asking the core question and a 
nice way of building our list of projects the 'fred' works on. It's 
not at all hard to extend this to find all the projects involving 
several people, or a project that 'jim' isn't working on etc. There 
are lots of useful enumeration methods for youto make use of.

Please, please don't give in to the temptation to give the 
CompanyProject class a method that simply returns the collection of 
employees and then just use some awful C programmer crap to treat 
objects as mere arrays of data. Encapsulation will save you time, 
effort and sanity in the long run. Any time you see code looking like
things whotsits dooberries do:[db| db subelements do:[ ......
run, screaming, away.

tim
--
tim Rowledge; [hidden email]; http://www.rowledge.org/tim
Useful random insult:- Proof that evolution CAN go in reverse.


_______________________________________________
seaside mailing list
[hidden email]
http://lists.squeakfoundation.org/cgi-bin/mailman/listinfo/seaside


_______________________________________________
seaside mailing list
[hidden email]
http://lists.squeakfoundation.org/cgi-bin/mailman/listinfo/seaside
Reply | Threaded
Open this post in threaded view
|

Re: Re: Smalltalk design advice - how toimplementgenericbi-directional pointers?

timrowledge

On 25-Dec-07, at 10:58 AM, Boris Popov wrote:

Except favourite pizza is usually some kind of aggregate of team members preferences depending on their number, seniority and drunkenness.

Hah! When you work in my team all your pizza are belong to me!


tim
--
10.0 times 0.1 is hardly ever 1.0.



_______________________________________________
seaside mailing list
[hidden email]
http://lists.squeakfoundation.org/cgi-bin/mailman/listinfo/seaside
Reply | Threaded
Open this post in threaded view
|

RE: Smalltalk design advice -howtoimplement genericbi-directional pointers?

Sebastian Sastre-2
In reply to this post by Aaron Rosenzweig-2
Hi Aaron,
 
    a comprensible reaction at first but see this subtlelty: the #parent and #children messages are there but you are the one who decide to use them in your objects. Probably the framework (who put there that accessors) has it's reasons to use them but they reasons are not your reasons nor excuses to lightly include them in your design. Mostly because a matter of being aware of the consequences of using them (or the inhability of using them).
 
    In smalltalk is useful to maintain allways a quote healthy skepticism with your own designs/code so you can question it every time you need. Question you own design is the informal way (and cheap way) of putting it into a test. Definitive and formal tests came with unit tests and usability tests.
 
    I know that components hard coupled to children with children loose copled to parents can scale in comlexity a lot. That's way I'm encouraged to speak about them. In any parent I sistematically use all the children I want/need, make convenience accessors or send direct messages. Also I make children neigh significant announcements or events everytime they happen (the event or announcement should be named in the perspective of the one who announces the event and *not* in what listeners wants to hear). This way anyone can be a listener. Most often will probably be some parent but sometimes other objects than parents or siblings may be interested in listening.
 
    I'm surprised about what you say about Apple. I'd expected more from them. Anyway they probably had some reasons for that it's java after all. I don't have any idea of where it leads but I smell I don't want to know. I do have an idea of Smalltalk/Seaside and I know they can allow me to go far.
 
    I think you where not naive but intuitive when thinking about #parent and #children must exists. It's obvius. The problem is not there but in the discern of the use of that obvious thing. As a rule of thumb I'll only allow myself to consider send a direct message to a parent when that children is only designed for that parent. But I'm aware that is tight coupling by definition and it's killing any chance of scaling/composing that. Also kills flexibility. So I also must have enough certainty that they will be not needed. To have certainty is not easy nor cheap so 100% of the times I prefer to make a new annoucement for the event, make the children to announce it when proper and make the parent to listen that event from that children and take proper reaction.
 
    Some people will "see" that send one "clever" direct message could save them a couple of dollars of doing a class adding a line in a listening method and the reaction and announcement methods but they are probably not seeing the consequences (so costs) of that "cleverness". They probably have to spend a couple of thounsands in refactoring sometime after when they realize they have an unflexible and unescalable design.
   
    cheers,
 

Sebastian Sastre



De: [hidden email] [mailto:[hidden email]] En nombre de Aaron Rosenzweig
Enviado el: Domingo, 23 de Diciembre de 2007 19:23
Para: Seaside - general discussion
Asunto: Re: [Seaside] Smalltalk design advice -howtoimplement genericbi-directional pointers?

Hi Sebastian. You just turned my world on its head :-)

On Dec 23, 2007, at 3:43 PM, Sebastian Sastre wrote:

Almost allways objects should not know about "their parents". But parents

often want to know what is going on with it's children even when the

children do not pass a message "directly" (hard coupling).


And I just read part of Ramon's blog that says the same kind of thing but with the specific example of Seaside components and the Announcements framework:


This is great, you both make valid arguments. While I understand what you say my past still haunts me so I popped open the system browser to examine "WAComponent" which is the general Seaside page component. It's just as you say. There is a "WAComponent>>children" message and a "WAComponent>>parent" message is AWOL.

"How can this be?" was my first guttural reaction.

You see, I'm a WebObjects weenie. "WOComponent" is to WebObjects what "WAComponent" is to Seaside. In a "WOComponent" you have "WOComponent.parent()" but you do not have "WOComponent.children()". It's the exact opposite of what you are recommending and what Seaside is doing. Combing through the cob webs of my recollection, I remember struggling with this notion years ago. I naively thought that both relationships should exist because there were times when I really wanted at the children of a particular component. And you know, if you dig into Apple's java code, you'll realize that they do have a hidden concept of children for a particular component, but they don't expose it in the public API. You kind of have to know your children when you are asked to generate the HTML response string and you want to give your "children" components a chance to "speak".

So, after I peel back the layers of the onion, I see that Apple consciously decided components should only know their parent. Yet, behind the scenes, they secretly have a way to get to the children. Kind of bizarre, I wish I knew what their design decision was but I don't. I had always just assumed there was a "very good reason" for modeling components the way Apple did, but maybe, in light of what you are telling me, there isn't one.

-- Aaron

_______________________________________________
seaside mailing list
[hidden email]
http://lists.squeakfoundation.org/cgi-bin/mailman/listinfo/seaside
Reply | Threaded
Open this post in threaded view
|

Re: Re: Smalltalk design advice - how to implementgenericbi-directional pointers?

Giovanni Corriga
In reply to this post by timrowledge
tim Rowledge ha scritto:
>  
> Please, please don't give in to the temptation to give the
> CompanyProject class a method that simply returns the collection of
> employees and then just use some awful C programmer crap to treat
> objects as mere arrays of data. Encapsulation will save you time, effort
> and sanity in the long run. Any time you see code looking like
> things whotsits dooberries do:[db| db subelements do:[ ......
> run, screaming, away.

Better yet, go look for the author with a blunt instrument in your hands
and a sparkle of righteous vengeance in your eyes.

        Giovanni, who got into the season's spirit

_______________________________________________
seaside mailing list
[hidden email]
http://lists.squeakfoundation.org/cgi-bin/mailman/listinfo/seaside
Reply | Threaded
Open this post in threaded view
|

Re: Smalltalk design advice -howtoimplement genericbi-directional pointers?

Aaron Rosenzweig-2
In reply to this post by Sebastian Sastre-2
As always, thank you Sebastian. I'll keep these thoughts in mind while learning Smalltalk and Seaside.

-- Aaron

On Dec 26, 2007, at 8:14 AM, Sebastian Sastre wrote:

    Some people will "see" that send one "clever" direct message could save them a couple of dollars of doing a class adding a line in a listening method and the reaction and announcement methods but they are probably not seeing the consequences (so costs) of that "cleverness". They probably have to spend a couple of thounsands in refactoring sometime after when they realize they have an unflexible and unescalable design.



_______________________________________________
seaside mailing list
[hidden email]
http://lists.squeakfoundation.org/cgi-bin/mailman/listinfo/seaside
Reply | Threaded
Open this post in threaded view
|

Re: Re: Smalltalk design advice - how toimplementgenericbi-directional pointers?

Sophie424
In reply to this post by timrowledge

"tim Rowledge" <[hidden email]> wrote

>  companyProjects select:[:proj| proj teamIncludes:  theEmpoyeeOfInterest]

Thanks, I think I have got the query-don't-store idea. My question here is,
assuming I want to know projects from a given employee (self):

what is #companyProjects? An instance variable every employee? Something
accessed through an instance variable of every employee (e.g. self company
projects)?

Or something accessed through a global of some sort e.g.
  Company singleton companyProjects?
or
  Project allInstances?

Something else?

Thanks.




_______________________________________________
seaside mailing list
[hidden email]
http://lists.squeakfoundation.org/cgi-bin/mailman/listinfo/seaside
Reply | Threaded
Open this post in threaded view
|

Re: Re: Re: Smalltalk design advice - how toimplementgenericbi-directional pointers?

timrowledge

On 26-Dec-07, at 8:04 PM, itsme213 wrote:

>
> "tim Rowledge" <[hidden email]> wrote
>
>> companyProjects select:[:proj| proj teamIncludes:  
>> theEmpoyeeOfInterest]
>
> Thanks, I think I have got the query-don't-store idea. My question  
> here is,
> assuming I want to know projects from a given employee (self):
>
> what is #companyProjects? An instance variable every employee?  
> Something
> accessed through an instance variable of every employee (e.g. self  
> company
> projects)?
>
> Or something accessed through a global of some sort e.g.
>  Company singleton companyProjects?
> or
>  Project allInstances?
>
> Something else?


All of the above :-)

I'd guess you might have a class for the Company - which could be as  
simple as a collection of employees and project or as complex as ....  
well a really complex thing with all sorts of legal and financial and  
administrative and organisational stuff. As long as you have at least  
the employees and projects for your current questions we're alright.  
In this case I'd suggest the the instance variable 'companyProjects'  
might be an OrderedCollection of (surprise!) the company's projects.  
Similarly the 'companyEmployees' would likely be an OrderedCollection  
of all the employees of the company.

'Company singleton' seems an unlikely thing to be useful; why would  
you write code that limited you to a single company? Have you no  
*ambition* :-)

As a counterpoint to the advice you've been getting to avoid  
bidirectional pointers, I should point out that there are of course  
case where that is exactly what you do need. For example in Sophie the  
content (the text and anchors and paragraphs and stuff) are kept in a  
tree structure and we need to be able to scan up the tree from a leaf  
to the root and to run down from any node to any leaf. We have to pay  
the price for that in complexity when editing the tree to keep all the  
pointing in the right direction. You would not believe how much pain  
has been caused by 'simple text editing'. I wrote a lot of it and I  
still don't believe it.

tim
--
tim Rowledge; [hidden email]; http://www.rowledge.org/tim
The problem with the gene pool is that there is no lifeguard.


_______________________________________________
seaside mailing list
[hidden email]
http://lists.squeakfoundation.org/cgi-bin/mailman/listinfo/seaside
Reply | Threaded
Open this post in threaded view
|

Re: Re: Smalltalk design advice - how to implementgenericbi-directional pointers?

Colin Putney
In reply to this post by Sophie424
On Mon, 24 Dec 2007 22:50:00 -0600, itsme213 wrote:

>
> "Colin Putney" <[hidden email]> wrote in message
>
>> just have projects keep a collection of employees. If at some point you
>> need to get the reverse - projects an employee is involved with, just
>> get all projects and select the ones that reference that employee.
>> Unless you have massive amounts of data, that'll work just fine.
>
> Where do you start this search?
>
> - A global e.g. someClass allInstances?
> - Some "ancestor" container object that all descendants store a pointer to?

I'd probably have an object that would serve as the root for all data
you care about. That might be called Company, but it might be something
abstract like Database or DataRoot. Then yes, I'd have a class variable
that provided global access to the sole instance or a collection of
instances of that class.

You'd get at it with "Company current" or something similar.

Colin
_______________________________________________
seaside mailing list
[hidden email]
http://lists.squeakfoundation.org/cgi-bin/mailman/listinfo/seaside
12