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