Externalize or Adapt? (ZPatterns)
Hi, suppose I have two Specialists (A and B) written independently each other. I need to use certain set of services provided by one Specialist (A) in other (B). I see two way: 1) Create common proxy for A, implementing public subset of A's interface and make B (and other possible clients) depending on that externalized interface. Problem: high dependency on A's external interface 2) Write specialized (A->B) pluggable adapters each time I need to use A's services in clients. Problem: M*N What do you think is ... hmmm... less painful way? Thanks, Mike
At 09:18 PM 6/8/00 +0800, Mike wrote:
Hi,
suppose I have two Specialists (A and B) written independently each other. I need to use certain set of services provided by one Specialist (A) in other (B). I see two way:
Let's take a specific example. Suppose I run a drop zone (a place where people go skydiving). I need to rent people parachutes, schedule planes, bill people for stuff, and so on. I have a ZPatterns accounting framework, with specialists named Invoices, Orders, Customers, and Products. I have a ZPatterns resource scheduling framework with specialists named Resources, Reservations, and ResourceUsers. How do I integrate the two? First, I'm going to create some ZClasses, and specialists to go with them: SkyDiver, Pilot, Airplane, FlightManifest, RentalTicket, RentalItem (w/subclass for Parachute, Helmet, and certain other specific items). Then I'm going to set up the Resources and Products specialists to access my own specialists for these ZClasses. (Notice that I am going in and directly customizing third-party specialists - that's a significant part of what specialists are there for.) I'm also going to set up both the Customers and ResourceUsers specialists to refer to my SkyDivers specialist, and the Reservations and Orders specialists will refer to the FlightManifests and RentalTickets specialists. * * * Let's stop a moment and consider this. Notice that we do not subclass SkyDiver from a Customer class and a ResourceUser class. What we are doing is saying is that Customer is a *role* that a Party (person or organization) plays in an accounting system, and ResourceUser is a *role* that a Party plays in a resource scheduling system. SkyDiver, then should implement a Party interface, allowing it to be *used as* a Customer or ResourceUser. Thus, SkyDiver can be used by *any* framework that needs to collaborate with a Party object, although it may have a new *role* in which the party will be used. For example, if I later add a curriculum management system to cover my skydiver training courses, I will want SkyDiver to fill the Student role. One of the reasons that ZPatterns was built to handle adding of propertysheets onto objects "without their knowledge", was so that Specialists could fill the role of *adapters* to "retrofit" objects into other frameworks. So if the "Student" role requires a Party to have, say, attendance records or grades, the Students specialist can include a propertysheet for that. The core SkyDiver object would not necessarily know about that propertysheet - unless of course I wanted it to - but the course management system would. Similarly, the billing system would want to know the SkyDiver's balance, but wouldn't care about his grades. (Unless I wanted to give him a discount based on them, in which case I'd add triggers for that.) Anyway, so back to our example... * * * I will also make some changes to each of these specialists to replace certain common UI snippets to be relevant to the kinds of things I'm dealing with. For example, in the Customers and ResourceUsers Specialists, I will replace the findForm method (if that's what it's called) to be a call to SkyDivers.findForm - a form that searches for skydivers by their ratings, certification, or freefall style, as well as name or phone number. Now, whenever the accounting framework's Invoices specialist wants to offer a "find invoice by customer" function, it can include Customers.findForm in its HTML, and it can then use that data to get what it sees as a "Customer" object, but is actually my "SkyDiver" object. The point is, it can find the invoices. (Or orders, or reservations, or whatever.) Notice, however, that for the most part, I do *not* have to change any of the code involving core competencies of the respective package. For example, the focus of the accounting package is not to be a customer database - its focus is invoices, balances, and the like. Similarly, the resource scheduling package is not really focused on the resources or their users, it's actually focused on the reservations and availability. So when you create a framework for distribution, even though you likely going to implement default collaborator specialists for things like Customers and Resources, they will mainly be there to illustrate the interface requirements of the framework. When someone actually uses the framework, they will take your sheet providers and any other "heavy lifting" machinery and plug it into their local specialists, then gut the collaborator specialist and wire it to the appropriate local specialist. You can look at Specialists as being of two kinds - "core competency" or "black box" Specialists, which are the meat of a framework, and "integration" or "white box" Specialists, which are meant to be ripped up by an application integrator in order to connect the framework into a larger application. In the context of an application, by itself, there is little distinction between the two. When you want to pull out and distribute parts of your application as reusable frameworks, you will need to make this distinction. Or, if you take an existing application and want to reuse part of it. Then you will need to decide which of its Specialists you will gut out and point to other objects and/or Specialists. Note also, and this is an important point for implementation, *Specialists are often created by wizards or wizard-like things*. They are stamped from a template and then edited. Or, they may be distributed as .zexp import files, coupled with Products for any of the classes they use. LoginManager is a specialist, which is why the recommended procedure for the PTK is going to be that the PTK membership system will create a raw LoginManager and then populate it with useful defaults that people can then change to more tightly integrate with their "end application".
1) Create common proxy for A, implementing public subset of A's interface and make B (and other possible clients) depending on that externalized interface. Problem: high dependency on A's external interface
2) Write specialized (A->B) pluggable adapters each time I need to use A's services in clients. Problem: M*N
Don't do either. Specialists are not classes. I repeat, Specialists are NOT classes. They are much more like modules than they are like classes. Think of a Specialist's methods as being like module-level functions. If you want to use one module in another, you just import it and call what you want. Or, to put it another way, think *composition*, not inheritance. You build by connecting blocks, not by making one super-block with all the powers of every kind of block. :)
What do you think is ... hmmm... less painful way?
Specialists model "aspects" of an application, or to put it another way, they represent a role that an object can play in an application. A single object can play many roles in an application. Correspondingly, an application which is built from multiple domain frameworks will have many Specialists accessing the same objects - but *providing different interfaces for that access*, based on the needs of the problem domain the specialist is a part of. If you follow this model for the design, the question you're asking should not arise in the first place. Or you might say, that's not a bug, it's a feature. :) The very feature, in fact, which is the motivation for me having written ZPatterns in the first place.
Great! Excellent! Thank you Phillip for a good explanation. I guess you should write something like this for the ZPatterns Wiki. One more thing we still need is a set of 'right hand' rules. Let me try to formalize ZPatterns-based design process. 1. Write scenario (client comes to skydiving office blah blah blah...) 2. Detect roles learning scenario (Orders, Planes, Students etc) 3. Create an empty (blackbox) Specialist for each role 4. Populate the specialists with methods implementing roles 5. Create (whitebox) specialists implementing scenario Thus, the object classes (Customer, Order) went nowhere. The things ruled by black specialists are just identity markers, without methods, types and names. That's right? Or is the object an specialist specific internal abstraction which never leaves specialist's frames and have methods and type just for specialist's fun? "Phillip J. Eby" wrote:
First, I'm going to create some ZClasses, and specialists to go with them: SkyDiver, Pilot, Airplane, FlightManifest, RentalTicket, RentalItem (w/subclass for Parachute, Helmet, and certain other specific items). Then I'm going to set up the Resources and Products specialists to access my own specialists for these ZClasses. (Notice that I am going in and directly
How you will set up those specialists? By adding get(Resources|Products)Specialist methods?
that a Party plays in a resource scheduling system. SkyDiver, then should implement a Party interface, allowing it to be *used as* a Customer or ResourceUser. Thus, SkyDiver can be used by *any* framework that needs to collaborate with a Party object, although it may have a new *role* in which the party will be used. For example, if I later add a curriculum management system to cover my skydiver training courses, I will want SkyDiver to fill the Student role.
Well, we have two specialists, implementing roles (again, who _actually_ implements the role, specialist or class, CustomerSpecialist or Customer?). Now, "SkyDiver ... *used as*" means we should: 1. subclass (not a good choice) 2. implement interface 2.1. by copying and pasting methods code (or whole methoids) 2.2. by proxiyng (SkyDiver has a references to actual Customer and ResourceUser) 2.3. by transmitting messages to SkyDiverSpecialist which will pass unhandled messages to CustomerSpecialist and ResourceUserSpecialist (this is a variant of 2.2. case) The 2.3. case means we should use objects without types (identity markers).
I will also make some changes to each of these specialists to replace certain common UI snippets to be relevant to the kinds of things I'm dealing with. For example, in the Customers and ResourceUsers Specialists, I will replace the findForm method (if that's what it's called) to be a call to SkyDivers.findForm - a form that searches for skydivers by their ratings, certification, or freefall style, as well as name or phone number.
How about Editor and Renderer plug-ins for each specialist?
lifting" machinery and plug it into their local specialists, then gut the collaborator specialist and wire it to the appropriate local specialist.
If I understand something at all, whitebox specialist (Script?) called SkyDiverSpecialist must have its own data source (rack) for SkyDiver class and use 2.2. strategy mentioned above. (I just see no other way). Thanks again, Mike
participants (2)
-
Mike -
Phillip J. Eby