History for AOPTutorial
??changed:
-
Doing AOP With !TransWarp (Under Construction)
The goal of this tutorial is to provide you with all the information
and examples you need to use !TransWarp's AOP tools. There are many
links from this tutorial which go to more technical Wiki pages about
the implementation of those tools. You don't kneed to know those
implementation details, however, if your goal is simply to do
aspect-oriented programming. The links are simply there if you are
curious, or if you want to create your own additions to the
!TransWarp family of tools (in which case you will need to know more
about the internal workings of the framework).
Also, as you go through the tutorial, you may find it helpful to try
the examples yourself in the Python interpreter, and inspect the
various aspects, classes, and objects created to learn more about how
the AOP system actually works.
Introduction: Components, Class Families, and Reuse
The Goal: Black Box Reuse
For decades now, the holy grail of reusability has been the notion
of the "component" - a black-box reusable piece of code.
Unfortunately, almost by definition, if you're going to reuse a
piece of code as a black box, you have to be able to use it without
changing it. That means that the code has to have been capable
from the start of doing what you want, even if it's by way of
parameterizing. What good are thousands of reusable components, if
none of them do precisely what you want, and you can't mix and match
the pieces from each that do what you want?
Many programming approaches offer partial solutions. Inheritance
lets you extend an existing class. Certain design patterns help to
define good hookpoints for overriding things in subclasses, or
factor out overrideable behavior into collaborator objects which
can be supplied by the (re)user of the code.
But those are just partial solutions, applicable only on a
class-by-class basis. A real solution for components must address
not just individual classes, but groups of classes that
collaborate. To be worthy of the name "component", they need to
be composable, as well. That is, it should be possible to build
components not only from classes, but from other components as
well.
In summary, a good component architecture must meet the following
"3 C's" requirements:
* Customization - Components must be customizable *without
changing them* (otherwise, you couldn't reuse the same component
for different things!)
* Collaborators - It should be possible to substitute or customize
a component's collaborator components or classes.
* Composition - It should be possible to use a component as a
collaborator in developing a larger component
How can we achieve such a component architecture? To meet the
"Customization" requirement, it would seem that we need a way to
create new versions of a component, perhaps stamping them out of a
template, or having some kind of factory object which can be told
what we want.
Further, if we could define *partial* templates, which were not
sufficient to create components by themselves, but which could be
combined with other partial templates, we would have the ultimate
in mix-and-match customizability, composition, and collaboration.
At this point, our actual unit of reuse becomes the factory or
template, rather than the component itself. The template does not
change, and can be distributed and reused in as many applications
as desired, but the individual components created by each (re)user
can be quite different.
This sounds a lot like a class and its instances, but at a higher
level. It's even different from the notion of a metaclass, because
metaclasses are templates for stamping out single classes, and what
we're talking about needs to stamp out entire families of classes,
with arbitrary numbers of classes involved in a single
instantiation.
So how could we create such a template?
The Aspect - A Template for Components
In !TransWarp, an "Aspect" is a template for components. It can be
complete or partial, and it can be combined with other aspects to
create new aspects.
An aspect can be thought of as being like a transparency used on an
overhead projector. Projecting light through the transparency
creates an image. Adding additional transparencies changes the
image, and the order in which they are placed can make a difference
in the image as well. Many different small transparencies (or
multiple copies of the same one) can be placed atop a larger
transparency, which may itself be being used as part of a still
larger transparency. Further, we can take the projected image and
turn it into a new transparency, or capture the image as a printed
photograph (a component).
In !TransWarp parlance, a "component" is a class and its
collaborators (which may themselves be components). To be useful,
these classes must of course be instantiated. To continue our
transparency analogy, this would be like using the finished
photograph (component) as a guide to building the thing depicted in
the photo (instance). To summarize our analogy:
* Transparency = Aspect = Component Template
* Photograph = Class Family = Component
* Thing In Photo = Class Instance = Component Instance
"Weaving" an aspect (instantiating a component) creates a brand new
class family: every class in it is specifically created for that
instantiation. This means that the same template can be used more
than once in the same application, to produce seperate (but similar)
class families if they are needed for different purposes.
In Python applications, most classes are created when the modules
that define them are imported. This does not change in a !TransWarp
application, but the process of creating those classes involves one
extra step. Let's begin with a very simple example::
from TW.Aspects import Aspect
class MyAspect(Aspect):
pass
MyComponent = MyAspect(name="MyComponent")
In this example, we create an aspect called 'MyAspect', and then
call it to instantiate a component class called 'MyComponent'. (We
pass in the name so that our created class will have its '__name__'
attribute set correctly.)
Now, if we want to create an instance of our class, we can do::
aComponent = MyComponent()
Aspects Are Not Classes
At this point, all the aspect stuff looks like meaningless
overhead, just as writing a class is meaningless overhead if you
have only one instance and will never reuse and customize it for
different purposes. Let's look at a slightly more meaningful
(although still trivial) example::
from TW.Aspects import Aspect
class MyAspect(Aspect):
class Thing:
def printMe(self):
print "Hi, I'm a", self.__class__.__name__
class Rock(Thing):
pass
class Dog(Thing):
pass
MyComponent = MyAspect(name="MyComponent")
This example creates 'MyComponent' again, but this time as a class
with three attributes: 'Thing', 'Rock', and 'Dog'. If we were to
type the code above into the Python interpreter, and inspect
the objects, we would see something like this::
>>> MyAspect
<Aspect MyAspect at 9812672>
>>> MyComponent
<class TW.Aspects.MyComponent at 949150>
>>>
Notice that 'MyAspect' is not a class, even though we created it
using a 'class' statement. It is an Aspect object. Let's take a
closer look::
>>> dir(MyComponent)
['Dog', 'Rock', 'Thing', '__doc__', '__module__']
>>> MyComponent.Rock
<class __main__.MyComponent.Rock at 949ef0>
>>> dir(MyAspect)
['__bases__', '__doc__', '__name__', 'componentBases', 'dictionaries']
>>> MyAspect.keys()
['Rock', 'Dog', 'Thing']
[358 more lines...]