[Subversion] / PEAK / docs / core_tutorial / binding.tex  

View of /PEAK/docs/core_tutorial/binding.tex

Parent Directory | Revision Log
Revision: 508 - (download) (as text)
Sun Sep 15 18:23:15 2002 UTC (21 years, 8 months ago) by pje
File size: 16310 byte(s)
Added another 2.5 (printed) pages of material, on composing applications,
the Service-Element-Feature pattern, and an introduction to the PEAK
package layout and API conventions.  Standardized on combining \citetitle
and \url to cite online references, since in printed form you can't click
on hyperlinks, and they don't seem to be working in .PDF versions either.
This set of additions still needs more proofreading, but I'd just as soon
check things in after every few pages for safety's sake.
\chapter{Defining and Assembling Components with \module{peak.binding}}

\section{Component-Based Applications}

What's in a component, anyway?  Why use them to develop software?  Software
developers have dreamed for decades of a future where applications could be
built by simply plugging together off-the-shelf components.  In some
development environments, this is at least partly reality today.  Many GUI
programming tools let you construct at least the visual parts of an 
application by assembling components.

The promised benefits of component-based development architectures include
reusability (and therefore less repetitive work), reliability (if each part
works separately, and they are assembled correctly, the whole assembly should
work), and ease of understanding/maintenance (because parts can be understood
separately).

To be useful, a component architecture must include ways of:

\begin{itemize}

\item connecting components to form an application,

\item packaging and distributing the components, and

\item separating the work of an application into components.

\end{itemize}

Let's look at how PEAK addresses these issues.











\subsection{Composing vs. Connecting}

Imagine a car.  It's composed from a variety of parts: the wheels, engine, 
battery, frame, and so on.  Some of these parts are also composed of parts:
the engine has a block, cylinders, pistons, spark plugs, and so on.

Each part in this "component assembly" can be a part of only one larger part:
its \strong{parent component}.  The hubcaps are part of the wheels,
and so they can't also be part of the engine.  (They wouldn't fit there in 
any case, but that's beside the point.)  Consider that screws or bolts may
be used in many parts of the car: each is part of only one other part of
the car, although more than one of the same \emph{kind} of part may be used
in other places.  In the UML (Unified Modelling Language) and in PEAK, this
kind of parent-child "assembly" relationship  is called \strong{composition}:
a component is being "composed" by assembling other components.

But in the UML and in real life, this isn't the only way of building things
with components.  It would be very inefficient if every light and accessory
in your car had to have its own, independent electrical system.  Ways of
\emph{sharing} components are needed.  In the car, wires, pipes, hoses, and
shafts serve to \emph{connect} the services provided by shared components to
the places where they are needed.  Note that such connections may be between
components at any level: wires carry electricity to every electrical part, no
matter how big or small.  In some cases, wires go to a major subsystem, which
then has internal wires to carry electricity inward to its parts, or to carry
signals between its parts.  In  the UML, these kind of "shared", "peer-to-peer"
connections are called \strong{associations}.














\subsubsection{Implementing Components in Python and PEAK}

In the Python language, components are Python objects, and composition and
association relationships are represented using objects' attributes.  Using
the \module{peak.binding} package, you'll create \strong{attribute bindings}
that  define what sub-objects will be created (via composition) or external
objects  will be referenced (via association) by each attribute of a component.

Of course, there are some important differences between software and the real
world.  In the real world, we have to actually build every part of the car
"ahead of time", and we must have one screw for every place a screw is needed.
In software, classes let us define the concept of a "screw" once and then use
it as many times as we want, anywhere that we want.

Also, with PEAK, bindings are "lazy".  What this means is that we can define
an "engine" class whose parts aren't actually created until they're needed.
We just list the parts that are needed and what attributes they'll be bound
to, and when the attribute is used, the part is automatically created or
connected, according to our definition.

Since each part "magically" appears the first time we want to use it, it's
as though it was always there.  It's as if your car was an empty shell until
you opened the door or looked in the window, at which point all the contents
magically appeared.  And then when you got into the car, the radio was just
an empty shell until you tried to turn it on, at which point all of its
internal components sprang into being and wired themselves together.

This "lazy" construction technique can speed startup times for applications
which are built from large numbers of components, by not creating all the
objects right away, and by never creating objects that don't get used during
that application run.










\subsubsection{Component Interfaces}

You can't just hook wires between random parts of your car and expect good
results.  In the same way, software components can only be "wired" together
if they have compatible interfaces.  A software component that expects to
send data to a "file" component must be connected to a component that
provides the same services a file would provide, even if the component is not
actually a "real" disk file.

A specific set of services that a component provides is called an
\strong{interface}.  Interfaces can denote a component's requirements, as well
as the guarantees that it provides when those requirements are met.  In PEAK,
interfaces are defined and declared using the Zope \module{Interface} package
(bundled with PEAK for your convenience).   This means that components you
create with PEAK's component architecture should also work in Zope 3's
component architecture.  (Which is important if you plan to build Zope
3-based applications and web services with PEAK.)

Interfaces in PEAK are used primarily as documentation and as a way of finding
compatible components and services.  When used with the Zope 3 component
architecture, interfaces can also be used to register adapters (which convert
a component from one interface to another), declare web views of application
components, and even define security restrictions based on interfaces.  

\begin{seealso}
\begin{itemize}

\item Remember, if you haven't already done so, be sure to check out
\citetitle{A Quick Introduction to Python Interfaces} at 
\url{http://www.zope.org/Wikis/Interfaces/InterfaceUserDocumentation}.  

\item And, if you're interested in developing Zope 3 applications and want to
learn more about what you can do with interfaces in the Zope 3 component
architecture, see \citetitle{Programming with the Zope 3 Component
Architecture} at \url{http://dev.zope.org/Wikis/DevSite/Projects/ComponentArchitecture/Zope3PythonProgrammerTutorialChapter1/
}.

\end{itemize}
\end{seealso}


\subsection{Applications = Components + Bindings}

For now, we're going to skip over the issue of packaging and distributing
components.  Since they're implemented as Python objects, we can use virtually
any of the standard techniques for packaging and distributing Python code,
such as the \module{distutils} package, or perhaps more elaborate systems such
as Gordon MacMillan's cross-platform \citetitle{Installer} package.

So let's move on to the third major aspect of a component architecture:
separating the work of an application or system into components.


\subsubsection{Why compose and connect?}

Is it realistic to expect to be able to define an application entirely in
terms of components?  And why would you want to do it in the first place?
Can't we just write an application the "old-fashioned way"?  That is,
import the exact components we want, and use them wherever and whenever
we want, instead of creating bindings to link them to a master "application"
component?

Well, you could, but one of PEAK's goals is to improve reusability.  Consider
this: all cars have engines, but different models of car have different goals
or requirements for their engines.  If we are creating a "Car" application,
wouldn't it be nice if we could switch out the "engine" component when we
get a new project, without having to maintain multiple versions of the code?
Perhaps for this project we need the "car" to be compact, or perhaps we need
a bigger engine for a station wagon this time.

How can we achieve such reusability?  It actually requires only a simple
coding convention: never write code in a function or method that directly
references another class.  Instead, define \emph{all} collaborations with
other classes by way of instance attributes.  This means that collaborating
component classes can be easily substituted in a derived class, substituting
a new "engine" class, for example.






While you can apply this technique of "hiding collaborators" in any
object-oriented language, PEAK takes the approach to a new level.  Because
PEAK attribute bindings can be used to programmatically define connections in
context, components can actually seek out their collaborators dynamically at
runtime, via configuration files or other sources.  This can be as simple
and ubiquitous as looking up what database to connect to, or as complex as
specifying strategy components to select algorithms that are optimal
for a specific deployment environment.

We're back to the "ilities" again: reusability, composability, extensibility,
and flexibility, in this case.  (Maybe portability, too.)  We even get a bit
of understandability and maintainability: isolating collaborations to attribute
bindings makes it a lot easier to implement the \citetitle{Law of Demeter} and 
write "Adaptive Programs". 

\begin{seealso}

\url{http://www.ccs.neu.edu/home/lieber/LoD.html} has more about the 
\citetitle{Law of Demeter}, and you can also see
\url{http://www.ccs.neu.edu/research/demeter/demeter-method/LawOfDemeter/object-formulation.html
} for its "object-oriented" version, if you'd like to know more about this
software quality technique.  PEAK takes this "Law" very seriously, insisting
that code which references even a collaborator class must do so via an
instance attribute or other "neighbor" as defined by the Law.

But, you don't have to know or follow the Law to use PEAK.  Your programs
just won't be as flexible.  All our example programs will obey the Law, 
however, so you'll see just how easy it is to do.

\end{seealso}

\newpage









\subsubsection{Services, Elements, and Features}

So what's an application made of?  The PEAK approach describes application
components in terms of their lifecycles and roles, as follows:

\begin{description}

\item[Services] \hfill \\ 
"Well-known" instance objects which exist for the lifetime
of the application.  For example, a database connection object in an
application could be a service component, and so could a top-level window
in a GUI application.  J2EE "session beans" and "message-driven beans" are
also good examples of service components.

\item[Elements] \hfill \\ 
Typically application domain objects or their proxies in
the user interface, Elements are created and destroyed by other Elements or
by the application's Services.  Typical Elements might be "business objects"
such as customers and sales, but of course any object that is the actual
subject of the application's purpose would be considered an Element.  In a
spam filtering program, for example, e-mail messages, mailboxes, and
"sender whitelist" objects would be considered Elements.

\item[Features] \hfill \\
Elements are usually composed of "feature" objects, which
may represent their properties, methods, associations, user interface views,
database fields, and so on.  Feature objects are often defined as an Element
class' class attribute, and shared between instances.  PEAK makes extensive 
use of feature objects to implement labor-saving techniques such as generative
programming.

\end{description}

This breakdown is called the \strong{Service-Element-Feature (SEF) Pattern}.
PEAK makes it easy to create components of each kind, but this pattern 
certainly isn't limited to PEAK.  You'll find Services, Elements, and Features
in almost any object-based application, regardless of language or platform.
But, the pattern is often implemented in a rather haphazard fashion, and
without the benefit of explicit "wiring" between components.


When you design an application using the SEF Pattern, your top-level 
application object is itself a Service.  You construct that service from
lower-level services, such as database connections, object managers,
and maybe even a top-level window object for a GUI application.  If your
application is to be web-based, or you're constructing a middle-tier
subsystem, perhaps it will be composed of web services or the equivalent
of J2EE session beans.

The top-level application object may provide various methods or "value-added"
services on top of its subcomponents' services, or it may just serve to start
them up in an environment that defines their mutual collaborators.  You may
find later that what you thought was an "application" component, is really 
just another service that you want to use as part of a larger application.
Fortunately, it's easy to change a PEAK component's bindings and
incorporate it into a larger system.

Defining the Elements of your application design is also fairly
straightforward.  Elements are the subject of what your application
\emph{does}.  A business application might have Customer elements, a web
server might handle Page elements, and a hard drive utility might manipulate
Partition elements.

Features, on the other hand, are usually provided by frameworks, and
incorporated into your application's Elements to create a specific kind of
application.  For example, a GUI framework might provide visual features that
allow mapping Element properties to input fields in a window.

While PEAK does or will provide many Element and Feature base classes you can
use to build your applications, these facilities are outside the scope of
this tutorial, which focuses on assembling an application's Service
components.  However, many of the techniques you'll learn here will be as
applicable to Element and Feature objects as they are to Service components.

So, let's get started on actually \emph{using} PEAK to build some
components, shall we?






\section{Getting Started with PEAK}

\subsection{Package Layout and API Conventions}

PEAK is installed as a set of Python packages, such as \module{peak.binding},
\module{peak.naming}, and so on, within the top-level \module{peak} package
space.  To help distinguish between the "public" and "private" portions of
the code, each subpackage includes an \module{api} module or subpackage, which
exports all of the commonly-used classes, functions, and constants that form
that package's public API.

The top-level package also includes an API subpackage, \module{peak.api}, which
contains each of the other subpackages' API modules, named for the subpackage.
In other words, the following two lines produce similar results:

\begin{verbatim}

from peak.api import binding

from peak.binding import api as binding

\end{verbatim}

For convenience, you can use \samp{from peak.api import *} to import all the
API subpackages.  Whichever approach you use to import the APIs you need,
you can access any \module{peak.binding} API class such as \class{Once}
by simply referring to \class{binding.Once}.  In this tutorial and all PEAK
documentation, including code examples, we'll refer to all APIs in this
fashion, by specifying the major subpackage and the class or function name.

\begin{notice}
Experienced Python programmers may wonder whether using
\samp{from peak.api import *} will cause all of PEAK to be imported.  In
fact, it doesn't, because the \module{peak.api} subpackage uses a 
"lazy import" mechanism.  Individual API subpackages like \module{peak.binding}
won't be imported until they're actually used by your code, to prevent wasteful
up-front loading of all the modules and classes.  See the source for
\module{peak.api.__init__} and the \class{lazyImport} class in
\module{peak.binding.imports}, if you'd like to know more about how it works.
\end{notice}

\section{Specifying Attributes Using Bindings}



\section{Composing Hierarchies with \class{binding.Component} Objects}



\section{Connecting Components by Name or Interface}


cvs-admin@eby-sarna.com

Powered by ViewCVS 1.0-dev

ViewCVS and CVS Help