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 |
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 |
"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 |
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 |
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 |
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 |
In reply to this post by Ramon Leon-5
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 |
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 |
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 |
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 |
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 |
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 |
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 |
> 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 |
Hi Sebastian. You just turned my world on its head :-)
On Dec 23, 2007, at 3:43 PM, Sebastian Sastre wrote:
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 |
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 |
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 |
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:
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 |
> 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 |
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 |
Free forum by Nabble | Edit this page |