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
|

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

Sophie424
I don't think I have grasped the "Smalltalk Way" to deal with this and am
looking for some design advice ...

I often repeat this kind of code to maintain relations that I have
implemented as bi-directional pointers (illustrative example only):

Object subclass: #House
   instanceVariableNames: 'rooms'

House>>addRoom: aRoom
    self basicAddRoom: aRoom
    aRoom basicAddHouse: self

House>>basicAddRoom: aRoom
    rooms add: aRoom

House>>removeRoom: aRoom
    self basicRemoveRoom: aRoom
    aRoom basicRemoveHouse: self

House>>basicRemoveRoom: aRoom
    rooms remove: aRoom

Object subclass: #Room
    instanceVariableNames: 'house'

Room>>addHouse: aHouse
    self basicAddHouse: aHouse

Room>>basicAddHouse: aHouse
    house notNil ifTrue: [house basicRemoveRoom: self]
    house := aHouse

etc.

How can I make this relation-management code generic, so I can easily add it
to any class with any named accessors, any number of times? I prefer to
avoid DNU approaches if possible as this code will be called in several
inner loops that need to run fairly tight. I thought traits might help but
now don't think so.

Thanks - Sophie



_______________________________________________
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 - how to implement genericbi-directional pointers?

Sebastian Sastre-2
Hi Sophie,

        this can sound basic but smalltalk has the virtue to allow you to
easily get the conceptual domain in a computer domain. Usually you take
concepts of real world, make a model in your mind of things you are
observing from it and then program them in "a computer". Maybe I didn't
observe the world enough but I don't know of real rooms in wich you can add
real houses in them, so it sounds to me that the conceptual model should be
reviewed even before to try to make a machine that can make that at
industrial scale.

        I use to take my time to mature the conceptual models before making
them in smalltalk, sometimes you have to make a little and see what happens
to get some feedback from the proposed design and define next steps. As
allways there are exceptions but the default for bidirectional knowledge is
not to do it and review design. Probably there is a more proper (so
extensible) way to achieve similar objetives.

        cheers,

Sebastian

 

> -----Mensaje original-----
> De: [hidden email]
> [mailto:[hidden email]] En nombre
> de itsme213
> Enviado el: Viernes, 21 de Diciembre de 2007 04:33
> Para: [hidden email]
> Asunto: [Seaside] Smalltalk design advice - how to implement
> genericbi-directional pointers?
>
> I don't think I have grasped the "Smalltalk Way" to deal with
> this and am looking for some design advice ...
>
> I often repeat this kind of code to maintain relations that I
> have implemented as bi-directional pointers (illustrative
> example only):
>
> Object subclass: #House
>    instanceVariableNames: 'rooms'
>
> House>>addRoom: aRoom
>     self basicAddRoom: aRoom
>     aRoom basicAddHouse: self
>
> House>>basicAddRoom: aRoom
>     rooms add: aRoom
>
> House>>removeRoom: aRoom
>     self basicRemoveRoom: aRoom
>     aRoom basicRemoveHouse: self
>
> House>>basicRemoveRoom: aRoom
>     rooms remove: aRoom
>
> Object subclass: #Room
>     instanceVariableNames: 'house'
>
> Room>>addHouse: aHouse
>     self basicAddHouse: aHouse
>
> Room>>basicAddHouse: aHouse
>     house notNil ifTrue: [house basicRemoveRoom: self]
>     house := aHouse
>
> etc.
>
> How can I make this relation-management code generic, so I
> can easily add it to any class with any named accessors, any
> number of times? I prefer to avoid DNU approaches if possible
> as this code will be called in several inner loops that need
> to run fairly tight. I thought traits might help but now
> don't think so.
>
> Thanks - Sophie
>
>
>
> _______________________________________________
> 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: Smalltalk design advice - how to implementgenericbi-directional pointers?

Sophie424

"Sebastian Sastre" <[hidden email]> wrote in message

> observe the world enough but I don't know of real rooms in wich you can
> add
> real houses in them,

OK, so if you change the #addHouse to #setHouse, or use other naming
conventions for 1-N, 1-1, M-N relations etc ... how would you implement the
recurrent code patterns?

I appreciate the point about bi-directional pointers, but find them useful
often enough that I would like to know how to solve the code duplication.

Thanks - Sophie



_______________________________________________
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 - how to implement genericbi-directional pointers?

Ramon Leon-5
In reply to this post by Sophie424
> How can I make this relation-management code generic, so I
> can easily add it to any class with any named accessors, any
> number of times? I prefer to avoid DNU approaches if possible
> as this code will be called in several inner loops that need
> to run fairly tight. I thought traits might help but now
> don't think so.
>
> Thanks - Sophie

You can't, what you're looking for are macro's to generate this boilerplate
for you and Smalltalk doesn't do that.  The Smalltalk way (if you must do
this) would be to reify the relationships as objects and do this at runtime
via dnu.  This is exactly what my Tsuanami package on SqueakSource/SqueakMap
does.  Take a look at the unit tests to see examples.  

However, a better solution, one that relies on real code and not dnu, is to
stop thinking objects are relational tables and start thinking in objects.
Rooms don't know what house they're in, tires don't know what car they're
on, products don't know what basket they're in, line items don't know what
order they belong to.  They don't need to, because if you have access to a
room/tire/product/line item, it's because you already have the
house/car/basket/order object.  The house/car/basket/order is the aggregate
root, it's the meaningful object. It's composite parts are only interesting
in relation to their parent and ideally only accessible through their
parent.  

Other objects should not reference the children of an aggregate, only the
aggregate itself.  This being the case, they don't need to know how their
parent is.  They're actually more composeable and better objects when they
don't.  I should be able to remove a room/tire/product/line item from one
house/car/basket/order object and put it into another house/car/basket/order
object without any weird side effects like on of them pointing to the wrong
parent.  

Objects are not tables and bi-directional relationships, in general, are not
desirable.  If I'm building a house, do I do this...

    aHouse addRoom: aRoom

Or this...

    aRoom house: aHouse

The simple answer is, make up your mind which one makes more sense in the
situation and do that one only.  Do not allow both, a model has little
meaning if it doesn't guide you in a particular direction and just allows
anything.

Ramon Leon
http://onsmalltalk.com

_______________________________________________
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?

Sebastian Sastre-2
In reply to this post by Sophie424
> -----Mensaje original-----
> De: [hidden email]
> [mailto:[hidden email]] En nombre
> de itsme213
> Enviado el: Viernes, 21 de Diciembre de 2007 13:03
> Para: [hidden email]
> Asunto: [Seaside] Re: Smalltalk design advice - how
> toimplementgenericbi-directional pointers?
>
>
> "Sebastian Sastre" <[hidden email]> wrote in message
>
> > observe the world enough but I don't know of real rooms in wich you
> > can add real houses in them,
>
> OK, so if you change the #addHouse to #setHouse, or use other
> naming conventions for 1-N, 1-1, M-N relations etc ... how
> would you implement the recurrent code patterns?
>
> I appreciate the point about bi-directional pointers, but
> find them useful often enough that I would like to know how
> to solve the code duplication.
>
> Thanks - Sophie
>

Sorry Sophie, I can't help you to go in that direction *and* maintain my
conscience clean at the same time

Best regards,

Sebastian

_______________________________________________
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 - how to implement generic bi-directional pointers?

cdavidshaffer
In reply to this post by Sophie424
itsme213 wrote:
> I don't think I have grasped the "Smalltalk Way" to deal with this and am
> looking for some design advice ...
>  
I understand your followup to some of the other replies.  Yes, it is
certainly reasonable to have "bidirectional pointers" for some kinds of
things (event tire-car...if it makes sense in your problem domain).  I
understand that you want a turnkey solution to building them.  I don't
think one exists because I think most developers find that they are
seldom needed (and if you need them a lot then you're either working way
outside my problem domain or you need to re-think your data model).

That being said, one answer (I can't say if it a "Smalltalk Way" or just
something that seems reasonable to try in the absense of macros, DNU
hacks etc) is to turn your relationships into objects.  So a House
doesn't hold a collection of rooms.  It holds a RoomHouseAssociation.  
This association makes sure that when a room is added, the room knows
which association (and therefore which house) owns it.  You can make
generic connectors of this sort (OneToManyAssociation,
ManyToManyAssociation) but I don't work in a problem domain where this
is useful.  If the few cases where relationships deserve first-class
objects, I create a new type for them.  I believe the domain-specific
version of this has appeared as a common OO design pattern (but the name
might not be "Association").  You should track down this design pattern
because it will undoubtedly give you a nice leg up on the API.

David

_______________________________________________
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 - how to implement genericbi-directional pointers?

Sophie424
In reply to this post by Ramon Leon-5

Ramon Leon-5 wrote
The simple answer is, make up your mind which one makes more sense in the
situation and do that one only.  Do not allow both, a model has little
meaning if it doesn't guide you in a particular direction and just allows
anything.
This is very helpful in making me think through, but ...

What does this do to UI? If I have a Seaside RoomComponent does this mean I should not allow "delete" through that component (awkward UI)? Or notify the owning house via events/announcements (feels wrong, since the room would not really even have been deleted until the house does its thing)? Or have the RoomComponent somehow invoke deleteRoom: aRoom on the owning house?

Thanks - Sophie
Reply | Threaded
Open this post in threaded view
|

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

Amos-15
In reply to this post by Sophie424
Hi Sophie,

I have to agree with most of the other people who responded -
generelly, try not to do it. Reconsider your design, it should be
possible to avoid it. There's a reason there's no convenient
"here's-one-we-prepared-earlier" way to do it. ;-)

Also, if you absolutely do have to do it, looking for a way to
"streamline it for next time" will only make you lean towards choosing
that method again when you consider your next design, instead of
trying to avoid it like the plague (as you probably should) and
looking for alternative designs.

Having said that, the closest I can come to thinking of a situation
where something like that is needed is in UIs, where a component needs
to be updated when something on the parent canvas changes, and vice
versa. I'm not sure whether that can be translated to what you need it
for, but the way it's generally done there (in VW, that is, haven't
really played around with UIs in Squeak yet) is via dependencies - an
object registers an interest in changes in the other object. Like I
said, I don't know whether that's applicable for your case, but there
you go ;-)

Cheers,

Amos


On 12/21/07, itsme213 <[hidden email]> wrote:

> I don't think I have grasped the "Smalltalk Way" to deal with this and am
> looking for some design advice ...
>
> I often repeat this kind of code to maintain relations that I have
> implemented as bi-directional pointers (illustrative example only):
>
> Object subclass: #House
>    instanceVariableNames: 'rooms'
>
> House>>addRoom: aRoom
>     self basicAddRoom: aRoom
>     aRoom basicAddHouse: self
>
> House>>basicAddRoom: aRoom
>     rooms add: aRoom
>
> House>>removeRoom: aRoom
>     self basicRemoveRoom: aRoom
>     aRoom basicRemoveHouse: self
>
> House>>basicRemoveRoom: aRoom
>     rooms remove: aRoom
>
> Object subclass: #Room
>     instanceVariableNames: 'house'
>
> Room>>addHouse: aHouse
>     self basicAddHouse: aHouse
>
> Room>>basicAddHouse: aHouse
>     house notNil ifTrue: [house basicRemoveRoom: self]
>     house := aHouse
>
> etc.
>
> How can I make this relation-management code generic, so I can easily add it
> to any class with any named accessors, any number of times? I prefer to
> avoid DNU approaches if possible as this code will be called in several
> inner loops that need to run fairly tight. I thought traits might help but
> now don't think so.
>
> Thanks - Sophie
>
>
>
> _______________________________________________
> 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: Smalltalk design advice - how to implement genericbi-directional pointers?

Ch Lamprecht
In reply to this post by Ramon Leon-5
Ramon Leon wrote:

> However, a better solution, one that relies on real code and not dnu, is to
> stop thinking objects are relational tables and start thinking in objects.
> Rooms don't know what house they're in, tires don't know what car they're
> on, products don't know what basket they're in, line items don't know what
> order they belong to.  They don't need to, because if you have access to a
> room/tire/product/line item, it's because you already have the
> house/car/basket/order object.  The house/car/basket/order is the aggregate
> root, it's the meaningful object. It's composite parts are only interesting
> in relation to their parent and ideally only accessible through their
> parent.  
>
> Other objects should not reference the children of an aggregate, only the
> aggregate itself.  This being the case, they don't need to know how their
> parent is.  They're actually more composeable and better objects when they
> don't.  I should be able to remove a room/tire/product/line item from one
> house/car/basket/order object and put it into another house/car/basket/order
> object without any weird side effects like on of them pointing to the wrong
> parent.  
>
> Objects are not tables and bi-directional relationships, in general, are not
> desirable.

Hello,

sorry if it gets too far OT for this list. Also the 'beginners' list might be
more appropriate for me to post on... However, I am making up my mind about this
for quite some time now, and maybe I can get some clues here.
Given the examples above, I understand your point. But thinking of the following
I don't see an obvious solution:
Let's say I have employees and projects where each employee might work on many
projects and each project has many employees working on it. Now in a RDB I
would have three tables one for each projects/employees and a linking table for
the m:n relationship, possibly containing details of the association.
Users of my application would expect to be able to search for all the employees
working on a given project and vice versa.
Trying to find an OO model to represent this, I end up with three Classes that
correspond to the three tables mentioned above ( maybe this is wrong already??).
Each of the project/employee instances would hold a collection of association
objects. Each association object would have instance vars to reference a project
and an employee. But that way I would have bidirectional links on both sides of
the association object...
I could avoid the collections of associations in employee/project and create
class-side methods in the association class instead answering collections of
associations referencing a given employee (or project). But that seems even
worse  to me - like simulating a database table.

I would be very thankful if anybody could point me into a better direction, ( or
recommend some books to read )

Regards, Christoph




_______________________________________________
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 - how to implement genericbi-directional pointers?

Colin Putney
On Sat, 22 Dec 2007 22:39:38 +0100, Ch Lamprecht wrote:

> Trying to find an OO model to represent this, I end up with three
> Classes that correspond to the three tables mentioned above ( maybe
> this is wrong already??). Each of the project/employee instances
> would hold a collection of association objects. Each association
> object would have instance vars to reference a project and an
> employee. But that way I would have bidirectional links on both sides
> of the association object...
> I could avoid the collections of associations in employee/project and
> create class-side methods in the association class instead answering
> collections of associations referencing a given employee (or
> project). But that seems even worse  to me - like simulating a
> database table.

It seems to me that you're trying too hard to make your model reflect
"reality." Don't worry about that. Instead, think about what you want
your application to accomplish. You want to be able to list the
employees that are assigned to a project? It's perfectly reasonable to
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.

Perhaps your application would be best served by a master list of
employee->project assignments. Maybe it needs a reverse index. Maybe
not. Maybe you need a date range associated with the assignment so you
can see what the employee has been invoved with in the past or his
schedule for the future. In that case, an temporal index might be
what's required.

The best thing to do is start by using the simplest model that captures
the information you need to present in your user interface, for the
smallest set of features that will be useful. As go forward, the UI
will get richer, and the model will evolve to meet the needs of the UI.

Colin


_______________________________________________
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 - how toimplement genericbi-directional pointers?

Ramon Leon-5
In reply to this post by Ch Lamprecht
> Let's say I have employees and projects where each employee
> might work on many projects and each project has many
> employees working on it. Now in a RDB I would have three
> tables one for each projects/employees and a linking table
> for the m:n relationship, possibly containing details of the
> association.

If you're trying to think in objects, the first thing you need to stop doing
is starting by thinking about how you'd do it in a relational database.
Relational database are meant to store data in such a manner that it'll work
for any model in any application, all data and no behavior.  That isn't OO
and that's not what you need to think about.

> Users of my application would expect to be able to search for
> all the employees working on a given project and vice versa.
> Trying to find an OO model to represent this, I end up with
> three Classes that correspond to the three tables mentioned
> above ( maybe this is wrong already??).

Why?  Seriously, you're still thinking relationally, you're talking about
nothing but data and searching.

> I would be very thankful if anybody could point me into a
> better direction, ( or recommend some books to read )
>
> Regards, Christoph

First, stop thinking about the data, programs aren't about data, they're
about behavior.  You have to ask yourself what is this program going to do,
what is its purpose, what does it accomplish for the user.  Is the program
about projects, or users, and which one is it going to be centered around?
What objects are you going to need most?  Modeling a program is about making
choices, picking a direction, and going for it.  Trying to keep
bidirectional links everywhere is just a way to avoid making a decision.  An
object model isn't a relational schema, it's not supposed to be agnostic,
it's supposed to be opinionated.  

Trying to make a model that works for all cases (relational db's) just leads
to a model that doesn't work well for any case.  Pick your most important
use cases and make the model work well for them, and make sure to let the
programs actual behavior dictate which solution is best.  Don't sit down and
design your object model like a relational schema where you name all the
attributes and try and normalize the data, rather, use CRC cards and figure
out how your objects collaborate, behaviorally.  Don't bother listing the
attributes, that doesn't matter, you can figure that out at implementation
time.

If you think hard enough, and find that your app really is just data with
little if any behavior, then you really just stop thinking about trying to
make a *good object model* and just dump the data to the screen in the
easiest manner possible, be it sql queries or an active record, it won't
really matter.  Object models are best suited for behaviorally complex
applications.

Ramon Leon
http://onsmalltalk.com

_______________________________________________
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 - how toimplement genericbi-directional pointers?

Sebastian Sastre-2
In reply to this post by Ch Lamprecht
Maybe I can add something to Ramon's aswer. See below.

> -----Mensaje original-----
> De: [hidden email]
> [mailto:[hidden email]] En nombre
> de Ch Lamprecht
> Enviado el: Sábado, 22 de Diciembre de 2007 19:40
> Para: Seaside - general discussion
> Asunto: Re: [Seaside] Smalltalk design advice - how
> toimplement genericbi-directional pointers?
......
> Given the examples above, I understand your point. But
> thinking of the following I don't see an obvious solution:
> Let's say I have employees and projects where each employee
> might work on many projects and each project has many
> employees working on it. Now in a RDB I would have three
> tables one for each projects/employees and a linking table
> for the m:n relationship, possibly containing details of the
> association.

Think about data and objects. May be this can help: consider to see data as
serialized objects. Data is stupid it can't do anything but to be printed
somehow. Objects instead are things (automatons) that have behavior. So they
are less stupid than data. You can ask them to be printed (serialized) in
different fashions: plain text, a pdf, utf-8, html, binary, etc.

So if you have this less stupid tool just take full advantage from it by
using them as if they where real instead to try to think you should make a
whole program with related instances of string like the RDB proposal.

By "as if they where real" I mean to have in mind that they have to honour
the real objects/concepts of the part of the real world you observed and
wanted to be modeled in a computer. This often means that you should observe
and re-observe the details in the real world to understand how to
review/factorize a design for it's next version.

All this in the example you gave could mean you put some object matching a
real concept in the middle to cut the circle and, give the effor you take,
take the cance of adding some value. I'll make a suposition to illustrate
this: I'll supose that is important for that application to know the use of
time of those employees for each project they are involved.

Given that I will "cut" that bidirectional knowledge by adding the concept
of AssignedEmployee. Now each project will have N assignedEmployees and each
assignedEmployee will know only one employee. In assigned employee the
project can add appointments affecting the use of time of that employee, a
meeting afecting all employees, etc.

You can ask to any project for the employees involved in it. And for the
"problem" of searching the projects in wich every employee is involved you
use some other object that maintains that convenient "query or index". Lets
say ProjectsManager knows wich projects it manages so it's easy to select
projects that satisfies an employee on them.

Smalltalk is mean to be heuristic and intuitive so it's legal to maintain
concepts as simple and clean as the simplified reality you observe.

*If* in the name of the cpu efficient usage you are tempted to make
something more "direct" to solve that problem by adding "relations", be
conscious that you are polluting that conceptual simplicity. So try to
select only those optimizations that guarantees your design to keep growing
in conceptual complexity. In other words remember that optimizations are too
often overestimated (decided before analizing real numbers/usage experience)
become pitfalls of it's own complexity and can easily prevent your model to
scale in conceptual complexity that you will need for sure if your
application has any success.

For the given case to solve that search "problem" an index could be easily
be made with a dictionary without pollutiong relationships in your model. If
your boss, customer or whatever changes it's mind about the importance of
that search you discard the index an maintain intact your model.

        cheers,

Sebastian

> Users of my application would expect to be able to search for
> all the employees working on a given project and vice versa.
> Trying to find an OO model to represent this, I end up with
> three Classes that correspond to the three tables mentioned
> above ( maybe this is wrong already??).
> Each of the project/employee instances would hold a
> collection of association objects. Each association object
> would have instance vars to reference a project and an
> employee. But that way I would have bidirectional links on
> both sides of the association object...
> I could avoid the collections of associations in
> employee/project and create class-side methods in the
> association class instead answering collections of
> associations referencing a given employee (or project). But
> that seems even worse  to me - like simulating a database table.
>
> I would be very thankful if anybody could point me into a
> better direction, ( or recommend some books to read )
>
> Regards, Christoph
>
>
>
>
> _______________________________________________
> 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: Smalltalk design advice - how toimplement genericbi-directional pointers?

Aaron Rosenzweig-2
In reply to this post by Ramon Leon-5
I'd like to thank Sophie and Christoph for asking an excellent  
question. And thanks to Ramon, Colin, David, Amos, Sebastian and  
others for lending a hand.

This thread strikes a real chord with me. I believe that if you model  
the domain effectively then the application feels like it can write  
itself. That is, spend time developing your critical domain objects  
and behavior before you start writing your app. If you find that in  
your app you are writing a lot of code, you are probably doing  
something wrong at which time you should revise your domain model.

What Sophie, Christoph, and myself are really asking is, "How should  
I go about developing my domain model". And more specifically "I have  
an idea of how to do this from my past experience, but given the new  
world of Squeak and Smalltalk, what's the best practice?"

Ramon mentioned "Class Responsibility Collaborator" models, or just  
"CRC cards" for short. That terminology is new to me and the idea is  
pretty cool. For the uninitiated, here's a few links:

General Definition:
===============
http://en.wikipedia.org/wiki/Class-Responsibility-Collaboration_card

Extended example:
===============
http://www.agilemodeling.com/artifacts/crcModel.htm

Bitmap scans of the actual handwritten CRC cards used in the creation  
of "HotDraw":
===============
http://c2.com/doc/crc/draw.html

Review of "Ecode", a CRC card design tool for Squeak:
===============
http://coweb.cc.gatech.edu/cs2340/3372

So I installed "Ecode" from SqueakMap into my 3.9 image taken from  
the Seaside one-click-experience. After running Ecode I could no  
longer "yellow" click on the desktop or other areas without getting  
an exception thrown... so I guess it is not compatible with the  
current Squeak. The idea of Ecode sounds pretty stellar and basically  
the kind of tool I want to use. Does anyone have suggestions about  
alternatives?

I've grown accustomed to having a visual tool where all I concentrate  
on is the domain objects, what their responsibilities are, and how  
they collaborate.

Let's talk again about the "Car" and "Tire" objects. I agree with  
Ramon that generally a Car needs to know about the Tire objects but  
the inverse is not important. A Tire can be placed on a different car  
and it shouldn't care. But what about a luxury car? Is that any  
different? Could be. The Tire might want to tell the car when its air  
pressure is low. For this it seems plausible that the tire might want  
to know that it is attached to the Car, that way it can send a  
message to the Car when it's time to replenish the air. You could  
still argue that the Car could "ping" the Tire every five minutes to  
find out the air pressure, or perhaps once each time you turn on the  
ignition, so it all depends.

I think Sophie was looking for some pattern along the lines of:

| car tire1 |
        car := Car new.
        tire1 := Tire new.
        car add: tire1 toBothSidesOfRelationshipWithKey: 'tires'.

The message "add: object toBothSidesOfRelationshipWithKey: string"  
would attach the tire to the car and, if you had modeled the inverse,  
it would tell the tire what car it was placed on. So the 'tires'  
relationship and the optional inverse 'car' relationship would have  
to be modeled somehow. Like maybe Tsunami or maybe Magritte but I  
don't know what I'm talking about here because I haven't cracked open  
those two Squeak frameworks yet.

Ramon, maybe you can clarify. You mentioned your Tsunami framework  
but in the same breadth alluded that we shouldn't use it? Or I  
misunderstand. In what situations *should* we use Tsunami or is it  
that you once felt it was necessary but later learned it isn't? In  
the Tsunami framework it mentions inspiration from the book  
"Streamlined Object Modeling" which has examples for both Java and  
Squeak. Would you say this is an excellent book for us new guys to read?

Thanks for listening,
-- Aaron


On Dec 23, 2007, at 2:40 AM, Ramon Leon wrote:

>> Let's say I have employees and projects where each employee
>> might work on many projects and each project has many
>> employees working on it. Now in a RDB I would have three
>> tables one for each projects/employees and a linking table
>> for the m:n relationship, possibly containing details of the
>> association.
>
> If you're trying to think in objects, the first thing you need to  
> stop doing
> is starting by thinking about how you'd do it in a relational  
> database.
> Relational database are meant to store data in such a manner that  
> it'll work
> for any model in any application, all data and no behavior.  That  
> isn't OO
> and that's not what you need to think about.
>
>> Users of my application would expect to be able to search for
>> all the employees working on a given project and vice versa.
>> Trying to find an OO model to represent this, I end up with
>> three Classes that correspond to the three tables mentioned
>> above ( maybe this is wrong already??).
>
> Why?  Seriously, you're still thinking relationally, you're talking  
> about
> nothing but data and searching.
>
>> I would be very thankful if anybody could point me into a
>> better direction, ( or recommend some books to read )
>>
>> Regards, Christoph
>
> First, stop thinking about the data, programs aren't about data,  
> they're
> about behavior.  You have to ask yourself what is this program  
> going to do,
> what is its purpose, what does it accomplish for the user.  Is the  
> program
> about projects, or users, and which one is it going to be centered  
> around?
> What objects are you going to need most?  Modeling a program is  
> about making
> choices, picking a direction, and going for it.  Trying to keep
> bidirectional links everywhere is just a way to avoid making a  
> decision.  An
> object model isn't a relational schema, it's not supposed to be  
> agnostic,
> it's supposed to be opinionated.
>
> Trying to make a model that works for all cases (relational db's)  
> just leads
> to a model that doesn't work well for any case.  Pick your most  
> important
> use cases and make the model work well for them, and make sure to  
> let the
> programs actual behavior dictate which solution is best.  Don't sit  
> down and
> design your object model like a relational schema where you name  
> all the
> attributes and try and normalize the data, rather, use CRC cards  
> and figure
> out how your objects collaborate, behaviorally.  Don't bother  
> listing the
> attributes, that doesn't matter, you can figure that out at  
> implementation
> time.
>
> If you think hard enough, and find that your app really is just  
> data with
> little if any behavior, then you really just stop thinking about  
> trying to
> make a *good object model* and just dump the data to the screen in the
> easiest manner possible, be it sql queries or an active record, it  
> won't
> really matter.  Object models are best suited for behaviorally complex
> applications.
>
> Ramon Leon
> http://onsmalltalk.com
>
_______________________________________________
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
> What Sophie, Christoph, and myself are really asking is, "How
> should I go about developing my domain model".

Good question. I think is maintaining the less complex objects that could
possibly honour the real life concepts/objects you want included. Then you
implement them in smalltalk and see what happen: or your proposed model is
enough or you need to refine/review.

 And more
> specifically "I have an idea of how to do this from my past
> experience, but given the new world of Squeak and Smalltalk,
> what's the best practice?"
>
The best pattern is no pattern: just the appropiate model for you. You said
that have an idea from the past knowledge. That can help as much as can
complicate things. Reuse the best of what you have and do not hesitate in
unlearning bad practices. A step backwards in a path that is leading you to
the wrong way could be a step forward. I use to "implode" bad
ideas/practices as soon as I can/become aware of. It could happen even
inside you own smalltalk designs.

In short: stop thinking relationalish. Start thinking objetish.

This is the same: be sure to stop trying to model the world arround you as
relationed data. That trend is founded in a mathematical model and not
reality. Use one of the main advantages smalltalk offers you: modeling real
concepts directly.

We are so used to model indirectly that we forgot we can model directly. By
modeling indirectly I mean: "to model reality using a relational model" that
is one degree of complexity guaranteed for everythig you try to think about.
Tables are layouted data. No matter they are html tables in a page or in a
RDBMS. They are not the ideas that provides fundation to the concepts. They
are like a photography of the concept. Objects are concepts.

To question about everything you observe and abstract behavior will help you
a lot more than identifying N to 1 relation between strings.

Sometimes you don't have the idea so you can start to explore by a first
aproximated proposed model. Then you test it and refactor what you need as
Boris said.

Is so much like real life that sometimes I'm amazed how easily we miss the
obvious.

...

> Let's talk again about the "Car" and "Tire" objects. I agree
> with Ramon that generally a Car needs to know about the Tire
> objects but the inverse is not important. A Tire can be
> placed on a different car and it shouldn't care. But what
> about a luxury car? Is that any different? Could be. The Tire
> might want to tell the car when its air pressure is low. For
> this it seems plausible that the tire might want to know that
> it is attached to the Car, that way it can send a message to
> the Car when it's time to replenish the air. You could still
> argue that the Car could "ping" the Tire every five minutes
> to find out the air pressure, or perhaps once each time you
> turn on the ignition, so it all depends.

Then you're talking about the need of a loose coupled relation. The tire can
announce that it's air pressure is low. Objects that are aware of the
existance of that tire can have the discern of wanting to register about
that event of that tire and react to it. Smalltalk user interfaces are made
on that basis from decades. Ramon's Leon has wroten a good post in it's blog
about Announcements framework.

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).

Take a look in events and announcements, that will help in those, not rare,
scenarios

Cheers!

Sebastian

_______________________________________________
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
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: Smalltalk design advice - howtoimplement genericbi-directional pointers?

Ramon Leon-5
In reply to this post by Aaron Rosenzweig-2
> Ramon mentioned "Class Responsibility Collaborator" models,
> or just "CRC cards" for short. That terminology is new to me
> and the idea is pretty cool. For the uninitiated, here's a few links:
>
> Bitmap scans of the actual handwritten CRC cards used in the
> creation of "HotDraw":
> ===============
> http://c2.com/doc/crc/draw.html
>
> Review of "Ecode", a CRC card design tool for Squeak:
> ===============
> http://coweb.cc.gatech.edu/cs2340/3372

This is one of the best techniques I've ever picked up from anyone, and it's
no accident those HotDraw CRC cards are scanned from hand written cards.
Ward Cunningham does the things he does for very good reasons.  Don't use
software to do CRC design sessions, it defeats the purpose and utility of
the technique.  3x5 index cards are one of the best thinking tools ever
invented, they let you design and simulate the execution of an object model
in minutes and being able to quickly write, pick up, move around, role play,
and discard the objects is vital to the usefulness of the technique.

Programs and white boards aren't nearly as useful as a handful of index
cards.  A little play time spent simulating several variations of your model
(cards are easy to throw away) will really solidify your ideas, finding the
good ones, and working through and tossing out the bad ones very cheaply,
before you've invested yourself in code.  Code is much harder to throw away,
you're more emotionally invested in it (which is why you don't use software
for CRC).

> Let's talk again about the "Car" and "Tire" objects. I agree
> with Ramon that generally a Car needs to know about the Tire
> objects but the inverse is not important. A Tire can be
> placed on a different car and it shouldn't care. But what
> about a luxury car? Is that any different? Could be. The Tire
> might want to tell the car when its air pressure is low. For

Nope, the tire should simply announce its air pressure and make no
assumptions about who's listening.  It's more composable that way.  I might
have the tire rigged up to a crazy 3 wheeled trike that doesn't care about
air pressure, and just because the tire can give air pressure readings
doesn't mean it should only work on cars it was made for.

> Ramon, maybe you can clarify. You mentioned your Tsunami
> framework but in the same breadth alluded that we shouldn't
> use it? Or I misunderstand. In what situations *should* we
> use Tsunami or is it that you once felt it was necessary but
> later learned it isn't? In the Tsunami framework it mentions
> inspiration from the book "Streamlined Object Modeling" which
> has examples for both Java and Squeak. Would you say this is
> an excellent book for us new guys to read?

It is an excellent book, one of my favorites.  It goes into great detail
about exactly how to maintain relationships and model rules in business
objects using a minimal set of patterns they found all rules could be
reduced to.  Every example in the book is done in both Java and Squeak,
which ends up being quite an advertisement for Squeak when you see just how
terse it is compared to Java doing the same thing.

Initially I was so enamored by the book that I adopted the patterns and went
overboard, using the patterns even when they weren't necessary because I
wanted consistency.  Here's an example, say I have a Person class, I'd do
the #name accessor like so...

name
    ^name

name: aName
    self testName: aName.
    set doSetName: aName.

testName: aName
    "throw exceptions for business rule violations"

doSetName: aName
    name := aName

This would give me an #x, #doSetX:, #testX: pattern of methods, each with a
particular purpose, the primary get/set accessors for normal use, internally
delegating to #textX: and #doSetX: which separate the testing and setting so
subclasses can easily override and extend them to add new rules and
behavior, or for allowing the running or bypassing of the rules when
necessary.  I really liked how they'd thought everything out, the rigor of
their model and detailed explanations of every technique.

I extended the Squeak IDE with my own create accessors routines that
generated all this boilerplate every time I added accessors.  What I learned
very quickly afterwards was all this stubbed out code, this scaffolding for
rules and subclass hooks, more often than not, went unused.  As my models
changed and I needed to refactor, I also realized the maintenance burden all
this extra code created for me, it made things hard to change and hard to
find.  I'd have 10 test methods but only two or three with actual rules in
them, the rest just empty stubs waiting to be used.

I grew tired of this, figured there had to be a better way, needed to rid
myself of the boilerplate code, and the need to maintain it.  That's when I
build Tsunami, I reified those relationships and patterns as runtime objects
and hacked doesNotUnderstand to deal with all this, I'd only add testX
methods I needed and they'd be picked up automatically, I'd get
bi-directional references maintained automatically, I'd get collections
encapsulated automatically via #addX #testAddX: #doAddX: #removeX
#testRemoveX: #doRemoveX:, and for a time, I was happy with it.

Later, after working with them for a while, I started to realize that
working with purely runtime generated constructs was a royal pain in the ass
(a lot of Ruby folk are find this out as well), debugging was more
difficult, stepping through the code was more difficult, nice things like
symbol completion and lookup to navigate the code didn't work so well,
because I'd gone to far, eliminated all the code including instance
variables and accessors in favor of runtime meta level constructs.  It
actually is possible to be too dynamic and too abstract.

I have a strong tendency to get sucked into solving the general case that
often distracts me from what I'm actually trying to accomplish (often
because I think it's more fun).  I'm learning to resist this urge, I've
learned to appreciate simplicity and direct solutions much more.  I still
use many of the patterns I picked up from streamlined object modeling, but
only when I actually need them.  I use real instance variables and simple
ordinary accessors (tools work much better now), and I never stub things out
ahead of time anymore.  I only write code that's actually needed, when I
actually need it.  Stubbing boilerplate stuff out for the future is a big
waste of time and gives you so much velocity in one direction that it makes
change harder.  Change should be easy, so write as little code as necessary
to achieve a desired behavior and no more.  

I used to assume bi-directional references, now I consider them an exception
to the rule, something to avoided if at all possible.  Direct references
flow down from parent to child, but events bubble up from child to parent.
That's how I see things these days anyway, who knows what I'll think next
year.

Ramon Leon
http://onsmalltalk.com




_______________________________________________
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?

stephane ducasse
Thanks ramon.
I agree with the debuggigng pain of DNU generalized :)

I read the streamlining.... book with the intention of turning it  
into a lecture.
And I realized that while I was sure that there was a value in there  
I could not
squisses its essence out of it. May be I did not practice enough the  
patterns
but I would have that the book was half of its size.
So your comments are confirming my gut feeling.

stef


On 24 déc. 07, at 08:39, Ramon Leon wrote:

>> Ramon mentioned "Class Responsibility Collaborator" models,
>> or just "CRC cards" for short. That terminology is new to me
>> and the idea is pretty cool. For the uninitiated, here's a few links:
>>
>> Bitmap scans of the actual handwritten CRC cards used in the
>> creation of "HotDraw":
>> ===============
>> http://c2.com/doc/crc/draw.html
>>
>> Review of "Ecode", a CRC card design tool for Squeak:
>> ===============
>> http://coweb.cc.gatech.edu/cs2340/3372
>
> This is one of the best techniques I've ever picked up from anyone,  
> and it's
> no accident those HotDraw CRC cards are scanned from hand written  
> cards.
> Ward Cunningham does the things he does for very good reasons.  
> Don't use
> software to do CRC design sessions, it defeats the purpose and  
> utility of
> the technique.  3x5 index cards are one of the best thinking tools  
> ever
> invented, they let you design and simulate the execution of an  
> object model
> in minutes and being able to quickly write, pick up, move around,  
> role play,
> and discard the objects is vital to the usefulness of the technique.
>
> Programs and white boards aren't nearly as useful as a handful of  
> index
> cards.  A little play time spent simulating several variations of  
> your model
> (cards are easy to throw away) will really solidify your ideas,  
> finding the
> good ones, and working through and tossing out the bad ones very  
> cheaply,
> before you've invested yourself in code.  Code is much harder to  
> throw away,
> you're more emotionally invested in it (which is why you don't use  
> software
> for CRC).
>
>> Let's talk again about the "Car" and "Tire" objects. I agree
>> with Ramon that generally a Car needs to know about the Tire
>> objects but the inverse is not important. A Tire can be
>> placed on a different car and it shouldn't care. But what
>> about a luxury car? Is that any different? Could be. The Tire
>> might want to tell the car when its air pressure is low. For
>
> Nope, the tire should simply announce its air pressure and make no
> assumptions about who's listening.  It's more composable that way.  
> I might
> have the tire rigged up to a crazy 3 wheeled trike that doesn't  
> care about
> air pressure, and just because the tire can give air pressure readings
> doesn't mean it should only work on cars it was made for.
>
>> Ramon, maybe you can clarify. You mentioned your Tsunami
>> framework but in the same breadth alluded that we shouldn't
>> use it? Or I misunderstand. In what situations *should* we
>> use Tsunami or is it that you once felt it was necessary but
>> later learned it isn't? In the Tsunami framework it mentions
>> inspiration from the book "Streamlined Object Modeling" which
>> has examples for both Java and Squeak. Would you say this is
>> an excellent book for us new guys to read?
>
> It is an excellent book, one of my favorites.  It goes into great  
> detail
> about exactly how to maintain relationships and model rules in  
> business
> objects using a minimal set of patterns they found all rules could be
> reduced to.  Every example in the book is done in both Java and  
> Squeak,
> which ends up being quite an advertisement for Squeak when you see  
> just how
> terse it is compared to Java doing the same thing.
>
> Initially I was so enamored by the book that I adopted the patterns  
> and went
> overboard, using the patterns even when they weren't necessary  
> because I
> wanted consistency.  Here's an example, say I have a Person class,  
> I'd do
> the #name accessor like so...
>
> name
>     ^name
>
> name: aName
>     self testName: aName.
>     set doSetName: aName.
>
> testName: aName
>     "throw exceptions for business rule violations"
>
> doSetName: aName
>     name := aName
>
> This would give me an #x, #doSetX:, #testX: pattern of methods,  
> each with a
> particular purpose, the primary get/set accessors for normal use,  
> internally
> delegating to #textX: and #doSetX: which separate the testing and  
> setting so
> subclasses can easily override and extend them to add new rules and
> behavior, or for allowing the running or bypassing of the rules when
> necessary.  I really liked how they'd thought everything out, the  
> rigor of
> their model and detailed explanations of every technique.
>
> I extended the Squeak IDE with my own create accessors routines that
> generated all this boilerplate every time I added accessors.  What  
> I learned
> very quickly afterwards was all this stubbed out code, this  
> scaffolding for
> rules and subclass hooks, more often than not, went unused.  As my  
> models
> changed and I needed to refactor, I also realized the maintenance  
> burden all
> this extra code created for me, it made things hard to change and  
> hard to
> find.  I'd have 10 test methods but only two or three with actual  
> rules in
> them, the rest just empty stubs waiting to be used.
>
> I grew tired of this, figured there had to be a better way, needed  
> to rid
> myself of the boilerplate code, and the need to maintain it.  
> That's when I
> build Tsunami, I reified those relationships and patterns as  
> runtime objects
> and hacked doesNotUnderstand to deal with all this, I'd only add testX
> methods I needed and they'd be picked up automatically, I'd get
> bi-directional references maintained automatically, I'd get  
> collections
> encapsulated automatically via #addX #testAddX: #doAddX: #removeX
> #testRemoveX: #doRemoveX:, and for a time, I was happy with it.
>
> Later, after working with them for a while, I started to realize that
> working with purely runtime generated constructs was a royal pain  
> in the ass
> (a lot of Ruby folk are find this out as well), debugging was more
> difficult, stepping through the code was more difficult, nice  
> things like
> symbol completion and lookup to navigate the code didn't work so well,
> because I'd gone to far, eliminated all the code including instance
> variables and accessors in favor of runtime meta level constructs.  It
> actually is possible to be too dynamic and too abstract.
>
> I have a strong tendency to get sucked into solving the general  
> case that
> often distracts me from what I'm actually trying to accomplish (often
> because I think it's more fun).  I'm learning to resist this urge,  
> I've
> learned to appreciate simplicity and direct solutions much more.  I  
> still
> use many of the patterns I picked up from streamlined object  
> modeling, but
> only when I actually need them.  I use real instance variables and  
> simple
> ordinary accessors (tools work much better now), and I never stub  
> things out
> ahead of time anymore.  I only write code that's actually needed,  
> when I
> actually need it.  Stubbing boilerplate stuff out for the future is  
> a big
> waste of time and gives you so much velocity in one direction that  
> it makes
> change harder.  Change should be easy, so write as little code as  
> necessary
> to achieve a desired behavior and no more.
>
> I used to assume bi-directional references, now I consider them an  
> exception
> to the rule, something to avoided if at all possible.  Direct  
> references
> flow down from parent to child, but events bubble up from child to  
> parent.
> That's how I see things these days anyway, who knows what I'll  
> think next
> year.
>
> Ramon Leon
> http://onsmalltalk.com
>
>
>
>
> _______________________________________________
> 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: Smalltalk design advice - howtoimplement genericbi-directional pointers?

Aaron Rosenzweig-2
In reply to this post by Ramon Leon-5
Thank you Ramon for your well thought out explanation. It helps a lot. Your "Tsunami" package sounds exactly what Sophie and I *thought* we wanted, but now after gaining your insights, it's probably not the way to go. Still, it sounds like Tsunami is a great technical achievement and could be used in other ways in the future. As an aside, you might want to copy some of your previous reply and paste it into the general description of Tsunami on SqueakSource so that the casual passer-by can weigh the pros and cons of the technique.

I have to ask this next question, let's reconsider the code snippet you offered:

On Dec 24, 2007, at 2:39 AM, Ramon Leon wrote:

name

    ^name


name: aName

    self testName: aName.

    set doSetName: aName.


testName: aName

    "throw exceptions for business rule violations"


doSetName: aName

    name := aName


I get it. You separate the concerns of "testing" and "setting" into methods that can be overridden. You also don't want to allow the general "setter" to save invalid data, so you construct it in a clever way to call the other two methods. Even though this all makes sense, it begs a question... "Maybe data validation happens too soon?"

Imagine "name" was bound up to a text field on an HTML web form. Let's say that the testing code required the first letter of the name to be in upper case to pass. So I type my last name into the field like so "rosenzweig" and click "submit". Because an exception gets thrown, we can imagine that we capture it and will display an error dialog to the user upon page reload. The error message could be quite verbose, even blurting out "you typed 'rosenzweig' but should have typed 'Rosenzweig'". However, the actual "name" value is still nil and you won't see "rosenzweig" in the text field, you'll see emptiness.

I would offer a slight variation. I would write your snippet like so:

name
    ^name

name: aName
    name := aName

validateName
    "Massage _name_ to pass validation if possible, otherwise
    throw exceptions for business rule violations. If an 
    exception is not thrown, set the corrected 
    (or unmodified) value"


The idea here is that "name" will always get set, regardless of what is typed, but it might not be saved to your persistence layer (object database - Magma, or relational database - GLORP). When you type in "rosenzweig" and hit "submit", one of the following could occur:

1) An exception will be thrown. A descriptive error dialog will appear on page refresh. The value of "name" will still show "rosenzweig" in the text field. The user sees what they typed but realize it didn't validate and can offer a slight modification then resubmit.

2) The method "validateName" is clever enough to realize that it can silently coerce (massage) "rosenzweig" into "Rosenzweig" on the user's behalf. The corrected value gets committed to the persistence layer and the user is taken to the next page.

This technique hinges on something knowing when and how to call "validate" methods on attributes. Perhaps the persistence layer will call "validateForSave" on each object before it actually does a commit. "validateForSave" would then, in turn, look over all of its attributes and call the "validateObject" method for each one, if it is defined.

-- Aaron

_______________________________________________
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?

Ramon Leon-5
> insights, it's probably not the way to go. Still, it sounds
> like Tsunami is a great technical achievement and could be
> used in other ways in the future.

I wouldn't call it a great technical achievement, but it was fun, and it
proved the value of unit testing to me.  I was failing miserably until I
switched to a test first style.  I think going overboard hacking
#doesNotUnderstand is a Smalltalk right of passage.  It's something you have
to do so you know when not to do it, it's very tempting to go crazy with it.

> As an aside, you might want
> to copy some of your previous reply and paste it into the
> general description of Tsunami on SqueakSource so that the
> casual passer-by can weigh the pros and cons of the technique.

Not a bad idea, I'll do that.

> I get it. You separate the concerns of "testing" and
> "setting" into methods that can be overridden. You also don't
> want to allow the general "setter" to save invalid data, so
> you construct it in a clever way to call the other two
> methods. Even though this all makes sense, it begs a
> question... "Maybe data validation happens too soon?"

One of the premises of the book is to never allow invalid data into the
object.

> Imagine "name" was bound up to a text field on an HTML web
> form. Let's say that the testing code required the first
>
> snip...
>
> I would offer a slight variation. I would write your snippet like so:
>
> snip...
>
> The idea here is that "name" will always get set, regardless
> of what is typed, but it might not be saved to your
> persistence layer (object database - Magma, or relational
> database - GLORP). When you type in "rosenzweig" and hit
> "submit", one of the following could occur:
>
> -- Aaron

You're assuming that I'd bind a business object directly to a form for
editing and wouldn't commit it unless it was valid, however, there's a flaw
in this technique, it allows the business object to be in an invalid state
which makes the further assumption that I'm required to commit to a
persistent store.  

Such is not always the case, especially when prototyping or writing small
apps that won't have more than 5-10 users. I use the image itself as the
persistence layer which means any changes made to the object *is* a commit
and is immediately available to other users.  I don't want biz objects in an
invalid state visible to others.  

When I edit objects, I bind a memento from the object to the form, and edit
the memento.  Only when the memento passes all necessary rules (which you
can test via the testX methods and gathering exceptions) would I allow the
memento to update the actual business object (took this technique from
Magritte, first time I'd seen a memento used this way).

Ramon Leon
http://onsmalltalk.com


_______________________________________________
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 - how to implementgenericbi-directional pointers?

Sophie424
In reply to this post by Colin Putney

"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?

Thanks





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