Section 2.2. LOCAL AND UNITARY STATEMENTS


2.2. LOCAL AND UNITARY STATEMENTS

Programming languages are about writing a structure of statements that a compilation or interpretation process will elaborate as a series of primitive directions. (The directions themselves will be a finite text, though their interpretation may be unbounded.) The earliest computer machine-language programs had a strict correspondence between the program text and the execution pattern. Generally, each programming language statement was both unitary and localunitary in that it ended up having effect in precisely one place in the elaborated program, and local in that it was almost always proximate to the statements executing around it.

The history (of this part) of programming languages has been about moving away from purely local and unitary languages and toward mechanisms that let the programmer separate concepts into pragmatic assemblages or modules, instead of being tied to saying things just where they happen. The first exceptions to locality were subprograms (i.e., procedures, subroutines, functions). Subprograms were a great invention, enabling abstracting out some behavior to someplace else. They have many virtues for separating concerns. For example, expertise in, say, Runge-Kutta methods could be embodied in the writer of the Runge-Kutta library. The application programmers were users of that library. They still had to know something about Runge-Kutta (when to use it, how to invoke it) and had to locally and explicitly call it in their code. The program was still unitary: It exhibited a direct correspondence between one statement in the programming language written and one sequence of machine instructions executed.

Inheritance (and related mechanisms like delegation) in object-oriented programming was another important introduction of non-locality. Executing inherited behavior is non-local. How explicit this execution was depended on whether the OO language used send-super or mixins.

Send-super systems like Java and Smalltalk allow the programmer to explicitly invoke the behavior of its parent class or classes without knowing exactly what behavior is being invoked. Adding behavior to classes higher in the class structure allows a limited form of quantified program statementsthat is, statements that have effect on many places in the underlying code. For example, suppose we wish to introduce a "display" aspect to a program about simulating traffic movement. We will want to make quantified statements like "Whenever something moves (executes its move method), the screen must be updated." Imagine that all things that move are descendants of the class of moveable-object. We can accomplish this with send-super inheritance if we have a cooperative base-class programmerone who will consistently follow directions. We make the behavior of the move method in movable-object be the display update, and we request that the programmers of derivative classes invoke send-super at the end of their code. This requires the derived class programmers to know that they have to do something, but it relieves them of having to know what exactly it is that they have to do. We're also restricted with respect to the locus of behaviorwe can ask programmers to do the send-super at the start of their code or at the end, but our directions probably need to be consistent throughout the system.

Requiring cooperation is not good enough. Programmers may fail to be systematically cooperative, and the base program may itself be already written or otherwise out of our control. For true AOP, we want our system to work with programmers who don't have to be thinking about every concern as they program. The behavior of the other concerns must be invoked implicitly. An early example of something close to obliviousness is mixin inheritance, found in MacLisp and Symbolics Lisp [6, 36]. With mixins, the derived-class functionality is determined by assembling the code of the derived class with the advice of its super classes. The aspect programmer can make quantified statements about the code by adding mixins, while the derived class programmer remains (almost) ignorant of these actions. The scope of quantification is controlled by which classes inherit the mixin. That is, we can quantify over the descendants of some superclass for a given single method. In the screen update example, adding an "after" mixin to movable-object's move accomplishes the automatic update. Except that class inheritance relationships are part of a class's definition, we would have an AOP system (and even this caveat has an exception [16]).

In general,

AOP can be understood as the desire to make quantified statements about the behavior of programs and to have these quantifications hold over programs that have no explicit reference to the possibility of additional behavior.

We want to be able to say, "This code realizes this concern. Execute it whenever these circumstances hold." This breaks completely with local and unitary demandswe can organize our program in the form most appropriate for coding and maintenance. We do not even need the local markings of cooperation. The weaving mechanism of the AOP system can, by itself, take our quantified statements and the base program and produce the primitive directions to be performed.



Aspect-Oriented Software Development
Aspect-Oriented Software Development with Use Cases
ISBN: 0321268881
EAN: 2147483647
Year: 2003
Pages: 307

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net