Re: [Zope-dev] Questions about implementing object models with ZPatterns
Phillip J. Eby wrote:
At 12:35 PM 11/1/00 +1100, Itai Tavor wrote:
Hi,
How do I implement gen-spec classes using ZPatterns and ZClasses? For example, I want to implement Person and Customer. Writing it in Python, it would be easy to subclass Person to get Customer, and Customer would inherit Person's properties and methods. If I instead create a specialist and ZClass for each class, do I use a SkinScript to remap Person's properties into Customer? And what about the methods - Customer can't inherit Person's methods, so I have to write methods in Customer which call the same methods in Person. This seems terribly complicated... am I missing something?
If you want subclasses, just subclass ZClasses. As for the remapping, etc., it depends on what you want to do. Keep in mind that Specialists are created per *role* or *interface*, not per class. You can have a "People" specialist that has a personRack and a customerRack in it, for example. Specialists are application- and usage-driven; you don't always end up creating specialists for every class in your object model.
Ok... I think I get the "Specialist per role, not per class" part. But I still can't make the jump from a class diagram to a ZClass/Specialist setup. I can solve some of it by subclassing ZClasses. So, if I need Customers and Resellers, I'll make a Specialist for each, and a Customer and Reseller ZClasses, both subclassed from Person which stores common properties for a person. This part is ok. But it gets more complex than that. Take this example: Every OrderLineItem object can have one or more Payment objects associated with it. There are 3 possible payment types - Check, Charge and BankDeposit, so I make a ZClass for each one, all subclassed from a general Payment ZClass. I create one Payments Specialist with 3 Racks. Where do I store methods that are specific to one payment type? In the Rack? I can't store them in the Specialist - it would be a mess, and I can't store them in the ZClass, because the ZClass doesn't know about the rest of the application. Actually, writing this down makes me realize that it could work... would Payments.getItem(some_payment_id).someMethod() call someMethod in the Rack if one exists, and the one in the Specialist if not? I guess that would make it very easy... subclassing moves into the Specialist. Another problem I'm having is how to store id's for different objects in the the same field. In the Payments example above this is not a problem, because all payments are supplied by the Payments specialist. But what about another example - Customers and Resellers are two totally different roles, so they each get a Specialist. the Payment object has from_id and to_id fields, and each of those can hold the id of a customer or reseller, or some special code to indicate the store. I could add from_type and to_type fields, or I could prefix the id with a code letter, but neither seem like a good solution. Is there a recommended approach for solving this problem?
(By the way, note that if you have a personRack and customerRack in the same specialist, they can share data plug-ins (such as SkinScript methods) placed directly in the specialist. The ranking order of the parent plug-ins in the child racks is determined by where you place a "Link to Parent Data Plug-Ins" in the racks' data plug-in lists.)
Another question: In Coad's examples, if a class has an 'n' order relationship to another class - say Order and OrderItems, Order would contain a list of OrderItems. Each OrderItem would contain the id of the Order it belongs to. It seems to me that it would be easier for Order to call OrderItems.getItemsForOrder(self.id), then I don't have to maintain a list in Orders, and if I add an OrderItem the Order will immediately know about it without me having to call it and tell it the OrderItem was added. Is there anything wrong with this approach?
Nothing at all. It's the recommended approach, actually. This is how you make frameworks work... by delegating to specialists responsible for a role in the application. That is, instead of the Orders system having to know anything about how order items are actually implemented, it can delegate that function to an OrderItems specialist that could have such things as DiscountItems, ReturnItems, and all sorts of other objects that implement an OrderItem interface. Similarly, rather than an Order providing its own interface for entering an order item, the UI code can be (should be) placed in the OrderItems specialist, and called by the Order object's add/edit forms.
Encapsulation at its very best. :) That's the ZPatterns way; the tools were specifically created to make it easy to do things the "right" way from a seperation-of-powers standpoint.
Thanks! I feel a little better now :) -- Itai Tavor "Je sautille, donc je suis." C3Works itai@c3works.com - Kermit the Frog "If you haven't got your health, you haven't got anything"
At 05:19 PM 11/1/00 +1100, Itai Tavor wrote:
Ok... I think I get the "Specialist per role, not per class" part. But I still can't make the jump from a class diagram to a ZClass/Specialist setup.
A class diagram contains all the roles - they're the lines between the classes. :) Look at it this way... if an object has a particular association role that it needs filled, and there is more than one kind of object that can fill that role, then presumably those objects must all implement a certain interface, right? You need to create a Specialist for that interface. In other words, there is usually one Specialist per collaboration interface -- which is NOT a one-to-one mapping with classes, since some classes may implement multiple interfaces, and some classes may all implement the same interface.
I can solve some of it by subclassing ZClasses. So, if I need Customers and Resellers, I'll make a Specialist for each, and a Customer and Reseller ZClasses, both subclassed from Person which stores common properties for a person. This part is ok.
What *role* do Customer and Reseller objects play in your system? It sounds to me like perhaps they play the role of "thing that places orders" or "thing that orders are shipped to". Depending on your application's functions, you could need as many as FOUR specialists: Customers Resellers BillableEntities ShippingDestinations Where the latter two specialists would contain a pair of Racks that mapped back to the Customers and Resellers specialists, respectively.
But it gets more complex than that. Take this example: Every OrderLineItem object can have one or more Payment objects associated with it. There are 3 possible payment types - Check, Charge and BankDeposit, so I make a ZClass for each one, all subclassed from a general Payment ZClass. I create one Payments Specialist with 3 Racks. Where do I store methods that are specific to one payment type? In the Rack? I can't store them in the Specialist - it would be a mess, and I can't store them in the ZClass, because the ZClass doesn't know about the rest of the application.
Huh? What do you mean by "methods that are specific to one payment type" in this context? What do payments do that requires knowledge of the rest of the application? If it's a problem-domain method, it belongs in the ZClass.
Actually, writing this down makes me realize that it could work... would Payments.getItem(some_payment_id).someMethod() call someMethod in the Rack if one exists, and the one in the Specialist if not?
No. DataSkins acquire only from the Specialist, not the Rack. However, you can use ClassExtenders in the Rack to provide methods to an object. But you *can't* override methods that already exist on the ZClass. This should not ordinarily be an issue since you should only be doing problem domain methods on your ZClasses anyway, and there should be no need to override them.
Another problem I'm having is how to store id's for different objects in the the same field. In the Payments example above this is not a problem, because all payments are supplied by the Payments specialist. But what about another example - Customers and Resellers are two totally different roles, so they each get a Specialist. the Payment object has from_id and to_id fields, and each of those can hold the id of a customer or reseller, or some special code to indicate the store. I could add from_type and to_type fields, or I could prefix the id with a code letter, but neither seem like a good solution. Is there a recommended approach for solving this problem?
See above, where I mention BillableEntities and ShippingDestinations. Having only one specialist per role means that you never have to worry about ambiguous identities. Please note, however, that at this stage of design you shouldn't be looking at how the references are going to be stored. At the abstract design stage, you would just have "Payor" and "Payee" attributes that are the actual related objects. When you write your SkinScript later, you can set up how the linkages work, using ID fields, or SQL columns, or whatever.
Phillip J. Eby wrote:
At 05:19 PM 11/1/00 +1100, Itai Tavor wrote:
Ok... I think I get the "Specialist per role, not per class" part. But I still can't make the jump from a class diagram to a ZClass/Specialist setup.
A class diagram contains all the roles - they're the lines between the classes. :)
That's a great statement :) It really helps me decipher my object model.
Look at it this way... if an object has a particular association role that it needs filled, and there is more than one kind of object that can fill that role, then presumably those objects must all implement a certain interface, right? You need to create a Specialist for that interface.
In other words, there is usually one Specialist per collaboration interface -- which is NOT a one-to-one mapping with classes, since some classes may implement multiple interfaces, and some classes may all implement the same interface.
How do you decide where to create collaboration Specialists? You wouldn't have one for every line in the object model, right? Do you use one only where there is more than one kind of object that can fill a role? How do you determine if a collaboration interface requires a collaboration Specialist, or can be done with a direct connection between the two class Specialists? For example, take an OrderLineItem object and a ShipmentLineItem object (one describes a quantity of a certain product added to an order, and the other that quantity of product being shipped). In the interface between OrderLineItem and ShipmentLineItem, an OrderLine can be seen as filling the role "thing that is shipped". OrderLineItem also fills the role "thing that is purchased" when it is added to an order. Would you create Specialists for each of these roles? Or, in the case of BillableEntities below, would you still use this Specialist if there were only Customers in the system (so, only one participant filling the "thing that places orders" role)? Are collaboration Specialists only used to interface participant-type objects?
I can solve some of it by subclassing ZClasses. So, if I need Customers and Resellers, I'll make a Specialist for each, and a Customer and Reseller ZClasses, both subclassed from Person which stores common properties for a person. This part is ok.
What *role* do Customer and Reseller objects play in your system? It sounds to me like perhaps they play the role of "thing that places orders" or "thing that orders are shipped to". Depending on your application's functions, you could need as many as FOUR specialists:
Customers Resellers BillableEntities ShippingDestinations
Where the latter two specialists would contain a pair of Racks that mapped back to the Customers and Resellers specialists, respectively.
The collaboration Specialist won't actually *do* anything, would they? They would only abstract the retrieval of participant objects, so that any object which requires a "ship_to" property will not have to know or care what kind of participant is referred to. Am I getting this right? (Of course, I could add PD methods to ShippingDestinations if I wanted to, like printShippingLabel, but in a basic application this Specialist doesn't seem to need to do anything at all). Also, these collaboration Specialists seem to serve to hide the fact that there are two types of participants. So, for example, a Payment object would just know "things that get billed". So how would I ask the Payments Specialist for "all payments made to resellers"?
But it gets more complex than that. Take this example: Every OrderLineItem object can have one or more Payment objects associated with it. There are 3 possible payment types - Check, Charge and BankDeposit, so I make a ZClass for each one, all subclassed from a general Payment ZClass. I create one Payments Specialist with 3 Racks. Where do I store methods that are specific to one payment type? In the Rack? I can't store them in the Specialist - it would be a mess, and I can't store them in the ZClass, because the ZClass doesn't know about the rest of the application.
Huh? What do you mean by "methods that are specific to one payment type" in this context? What do payments do that requires knowledge of the rest of the application? If it's a problem-domain method, it belongs in the ZClass.
Ok, I get this now. This question was based on my lack of understanding of how a ZPatterns application is structured. A Payment object needs to access other parts of the application - for example, to get a UI snippet from BillableEntities. I thought that ZClasses are supposed to be self-contained, without links to the rest of the app, while methods that link parts of the app together belong in the Specialists. I can see I was totally wrong in this.
Please note, however, that at this stage of design you shouldn't be looking at how the references are going to be stored. At the abstract design stage, you would just have "Payor" and "Payee" attributes that are the actual related objects. When you write your SkinScript later, you can set up how the linkages work, using ID fields, or SQL columns, or whatever.
The reason I'm thinking about implementation now is that, this being my first ZPatterns project, I want to make sure my design is actually implementable. So I keep jumping forward and trying to implement pieces of the design to see how they actually work. This is a quote from a previous answer you gave to a question:
...That is, instead of the Orders system having to know anything about how order items are actually implemented, it can delegate that function to an OrderItems specialist that could have such things as DiscountItems, ReturnItems, and all sorts of other objects that implement an OrderItem interface. Similarly, rather than an Order providing its own interface for entering an order item, the UI code can be (should be) placed in the OrderItems specialist, and called by the Order object's add/edit forms.
This raises an implementation question: The UI for adding an OrderItem is implemented in the Specialist, right? What if OrderItems has two Racks - Item (for standard items) and CouponItem, each requiring a different 'add' form? Would you place the 'add' UI code for each one in the Rack? Or use a single UI method that customizes itself based on the type of object being added? One last thing - I just want to say I really appreciate all your help (and that of other people who've answered my recent questions) - I'm trying learn both formal object model design and ZPatterns at the same time, and it's a slow and frustrating process - for me, but also I'm sure to people trying to deal with my questions. Thanks! -- Itai Tavor "Je sautille, donc je suis." C3Works itai@c3works.com - Kermit the Frog "If you haven't got your health, you haven't got anything"
participants (2)
-
Itai Tavor -
Phillip J. Eby