[Zope-dev] Externalize or Adapt? (ZPatterns)

Phillip J. Eby pje@telecommunity.com
Thu, 08 Jun 2000 12:30:28 -0500


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.