You are not logged in Log in Join
You are here: Home » Members » Phillip J. Eby's Zope Center » My Wikis » TransWarp Wiki » AOPTutorial

Log in
Name

Password

 
 

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...]