ACCU Home page ACCU Conference Page
Search Contact us ACCU at Flickr ACCU at GitHib ACCU at Google+ ACCU at Facebook ACCU at Linked-in ACCU at Twitter Skip Navigation

pinProgramming - Abstraction by Design

Overload Journal #77 - Feb 2007 + Programming Topics + Design of applications and programs   Author: Nigel Eke
Nigel Eke acts as a guide to aspect oriented programming using AspectJ as an example.

Two unrelated paragraphs...

Schadenfreude (pronounced 'Shar-den-froy-der'). This is a German word that has no direct translation into English. Look it up in a dictionary and it may get translated to 'gloat', but this is not the complete translation. Literally it means 'damage-joy'. Specifically it means 'taking delight in another's misfortune'.

The physicist Richard Feynman once served on a commission to select textbooks for schools. Many of the books were introducing 'the new maths' and set theory in particular. Feynman's comments on set theory were that it comprised new definitions for the sake of definition, a perfect case of introducing words without introducing ideas. ... specialised language should wait until it is needed, and the peculiar language of set theory is never needed. [Gleick]

This article introduces aspect-oriented programming, so why start with these two examples, especially when they appear to have no relationship to programming, let alone aspect-oriented programming?

If we take a look at programming, programming languages, and their history, a couple of concepts come to the fore. One is related to how we program, regardless of language. The second is related to what the programming languages themselves provide.

Before we dive into to some of the new concepts covered by aspect-oriented programming, I would like summarise what has happened during the development and creation of computer programming languages. However, if you really wish to get straight into the details then skip to the "Aspect-oriented programming" section.

Let us start by asking "What are we doing when we program computers?" In fact there is more than one answer to this question depending on your perspective. Generally we are telling the computer to follow a particular set of instructions to produce some output based on various information input into the program. But the program itself is not just a set of instructions to be executed. The program also comprises source code, which we have to read. The source code informs us, both as developers and future maintainers, what the intentions were when we originally wrote the code. Let us explore both of these briefly.

Programming a set of executable instructions

Program instructions are generally written in a programming language. They are translated by the language's compiler into the processor codes for a target processor. The codes get loaded into the target computer's memory and get executed. I use the term 'compiler' liberally here to cover genuinely compiled and interpreted languages.

If we take a look at the history of this translation step, a common theme emerges.

  • Originally programs were loaded into the computer's memory by 'programming' through toggling the switches on the front panel of the computer. A program was then executed by loading the computer's program-counter register with the address of the start of the program. Once upon a time this used to be the only way to load a program into a computer - even if the 'toggled' program was just run once to read further programs from paper tape or disk-drives. As a minor aside - the external article [Wiki(1)] serves as tongue-in-cheek reminder of these 'Real Programmer' memories.
  • Assembler languages provide mnemonics to represent the processor instruction codes. This enables the programmer to concentrate in terms of what the processor instructions actually do, rather than the binary code needed to perform those instructions. It also allows memory locations to be accessed by some descriptive name, rather than a physical address. A level of confidence is provided over and above the toggling of switch settings. It is easier to remember ADD #2, R1;1 rather than loading two words of instructions with the values 65C1 and 0002.
  • High level languages - such as FORTRAN and COBOL - remove any dependency on the underlying processor architecture. This means that the programmer is no longer concerned with the instructions need to control the processor, but can focus on what the program itself is intended to do. High-level languages give us a level of confidence over and above having to choose the right registers for a processor instruction, or device address for an i/o operation. Writing:
     IF Salary > 10000 GOTO nobonus;
       LET Salary = Salary + Bonus;
     nobonus:
        
    is clearer, and less error-prone, than:
       MOV Salary, R0;
       SUB #2710, R0; 'Often Hex or Octal!
       BGT nobonus;
       MOV Salary, R0;
       ADD Bonus, R0;
       MOV R0, Salary;
     nobonus:
        
  • Structured languages - Algol-68, Coral-66 among others - are also independent of the underlying processor architecture. Their constructs provide more rigour than the procedural counterparts above, when describing the program flow. They also enable a divide and conquer approach to writing the program. This results in cleaner modularity and less of the monolithic spaghetti-code programs that tended to result from using the high-level languages.
  • Object-oriented languages take this one stage further. These languages enable the program to use terms which focus on problem domain objects, e.g. Customer, Account or solution domain objects, e.g. ButtonEventAdaptor. This has the benefit that the modular responsibilities become more clearly defined than with structured approaches. Object-oriented programming also means that the relationships between objects are shown through inheritance, composition and aggregation. One of the benefits of inheritance is that it enables code reuse. Object-oriented programming languages take structure one stage further and give us a level of confidence regarding a module's responsibilities.
  • There are also a few 'specialist' languages. Logic programming languages, such as Prolog, work simply by defining a set of predicates, goals and sub-goals, in order to seek out a solution. An automatic tree-search is performed for the prime goal. APL (Array Programming Language [Wiki (2)]) uses symbols, rather than text, to dictate what the program will do. These languages provide a method to write a solution in a form that serves a specific classes of problems.

Each one of these phases in the history of programming languages adds a level of abstraction over and above the previous level. Each advance or change in programming language provides a different way to describe the solution; each provides a different view of that solution with respect to the original problem.

This can also be said of the many programming languages that have not been given a mention [Wiki (3)]. The creator(s) of these languages must have a felt the need to design a language which provided something not available in other languages, albeit a new feature, new keywords, even a new syntax simply to make the compiler writer's job easier.

Program source code

Regardless of the language being used, the program source code also conveys other information.

Comments provide more information about a block of code, i.e. its intention - or perhaps we should say original intention, given the number of times code and comments become out of synch. Sometimes comments make statements about the requirements being satisfied.

Variable naming tells us what information is being held in that variable. Procedural naming tells us something out the task being performed by the procedure. Class naming tells us something of the responsibilities of that class and, perhaps, expected functions performed on objects of that class.

Preamble summary

So how does this relate to Schadenfreude and Feynman?

Correct, concise and consist use of names helps make a clearer program. A clear program provides us with confidence that the solution we're writing actually does work.

It will be better if we can use the term 'Schadenfreude' rather than a lengthy textual description. Of course I realise Schadenfreude is not the best description to use for an English speaking audience - again this comes back to choosing the right name in the right context.

Nevertheless, it is the vocabulary provided by the language keywords and how we use our naming that helps us write, understand and maintain programs.

The choice of the right tools for the job also helps us deliver clearer solutions to the problem. This means not only choosing the right language(s) but also the right subset of their keywords to satisfy the demands of your programming environment. Do not use 'set theory' just because it is there. A clever generically programmed C++ solution may not be appropriate if the remaining people in the development group are still wet behind the ears programmers.

Aspect-oriented programming

The history and comments above are all a rather long preamble into the main topic of this article. As we walk through some of the key elements of aspect-oriented programming keep in the back of your mind how each of the stages of development of other programming languages adds to the ability to design and code a solution.

The additional abstraction mechanisms introduced with aspect-oriented programming extend those provided in the aforementioned languages, particularly some concepts introduced in object-oriented languages.

Aspect-oriented programming is based on separation of concerns, i.e. breaking a program into distinct areas of functionality. In fact this is a common concept with structured programming and object-oriented programming languages, but aspect-oriented programming takes this view one stage further.

Even though the inheritance of object-oriented programming gives us code reuse we still find times when code is duplicated. For example, tracing call paths through code, where we are concerned about the aspect of 'tracing', or the bracketing of start and commit where we are concerned with making certain our database transactions are atomic.

Hello World example

I'm not intending this article to cover every detail of aspect-oriented programming, but rather highlight the coding and design abstractions that it provides. Nevertheless, it is worthwhile diving straight in to a simple AOP Hello World example.

Java and AspectJ (an AOP language whose syntax closely follows that of Java) will be used in the example languages. Aspect-oriented programming, however, is not limited to Java and AspectJ - more on this later.

In this example we want to trace the execution of the methods in two classes, Name and Address.

The core classes look like Listing 1 - a couple of attributes and their corresponding getters and setters.

 package com.nigeleke.accuexample.classes;
 public class Name {

   public Name (String forename, String surname)
	{forename_ = forename; surname_ = surname;}

   public String getForename() {return forename_;}
   public void setForename(String forename) {
	  forename_ = forename;}

   public String getSurname() {return surname_;}
   public void setSurname(String surname) {
	  surname_ = surname; }

   private String forename_;
   private String surname_;
 }

 package com.nigeleke.accuexample.classes;
 public class Address {

   public Address (String street, String town)
	 {street_ = street; town_ = town;}

   public String getStreet() {return street_;}
   public void setStreet(String street) {
	  street_ = street;}

   public String getTown() {return town_;}
   public void setTown(String town) {
	  town_ = town;}

   private String street_;
   private String town_;
 }
 
Listing 1

If we add tracing by more traditional methods we end up with Listing 2.

 package com.nigeleke.accuexample.classes;
 public class Address {

   public Address (String street, String town) {
	 System.out.println("Entered Address:ctor");
	 street_ = street; town_ = town;
	 System.out.println("Exiting Address:ctor");
   }

   public String getStreet() {
	 System.out.println(
		"Entered Address::getSteet");
	 System.out.println(
		"Exiting Address::getSteet");
	 return street_;
   }

   public void setStreet(String street) {
	 System.out.println(
		"Entered Address::setSteet");
	 street_ = street;
	 System.out.println(
		"Exiting Address::setSteet");
   }

   public String getTown() {
	 System.out.println(
		"Entered Address::getTwin");
	 System.out.println(
		"Exiting Address::getTown");
	 return town_;
   }

   public void setTown(String town) {
	 System.out.println(
		"Entered Address::setStreet");
	 town_ = town;
	 System.out.println(
		"Exiting Address::setStreet");
   }

   private String street_;
   private String town_;
 }
 
Listing 2

Similar coding is also inserted into the Name class. As you can see, not only does it detract from the real work of the class, it is also pretty repetitive. Not only that, but the repetition is across classes as well as the methods within a class. This is therefore error-prone - you only have to look at the (deliberately - yes, honest!) bugs introduced in getTown() and setTown().

The common theme that we're considering here is one of tracing. Or, to put this in terms used in AOP, the concern we have is the aspect of tracing. (Note how the keywords of the AspectJ language help us with our abstraction).

So let's, first off, forget all the println statements from Address and go back to the original class, then define an aspect for performing tracing.

The aspect looks very much like a class definition:

 package com.nigeleke.accuexample.aspects;
 public aspect Tracing {

   // (1)
   public pointcut constructor() :
	 execution(
		com.nigeleke.accuexample.classes.*.new(..));

   // (2)
   public pointcut anyMethod() :
	 execution(
		* com.nigeleke.accuexample.classes.*.*(..));

   // (3)
   before() : constructor() || anyMethod() {
	 System.out.println("Entered ctor or method");
   }

   // (4)
   after() : constructor() || anyMethod() {
	 System.out.println("Exiting ctor or method");
   }
 }
    

Declarations (1) and (2) declare pointcuts. The pointcuts are used in the selection of join-points, i.e. the locations in the OO code that are of interest to us. For tracing, this will be each time we enter or exit a constructor or a method.

Declaration (1) states that we are interested in all constructors of all classes within the com.nigeleke.accuexample.classes package; declaration (2) states that we are interested in all methods in all classes, again within the com.nigeleke.accuexample.classes package. Specifically they state we are interested in the execution of the package's constructors or methods.

Statements (3) and (4) are known as advices. They inject their advice, i.e. the body of code associated with them, at the join-points defined by the pointcut filter expressions.

Statement (3) is saying that, before we execute a constructor (pointcut 1) or execute a method (pointcut 2) we will print out a trace to say we have entered. Similarly statement (4) is saying that, after we've executed the constructor or method will will print out a tracing to say we're exiting.

So when we run this code:

 Address address = new Address("aStreet", "aTown");
 address.setStreet("bStreet");
 address.setTown("bTown");

 Name name = new Name("aForename", "aSurname");
 name.setForename("bForename");
 name.setSurname("bSurname");
    

we get:

Entered ctor or method
Exiting ctor or method
Entered ctor or method
Exiting ctor or method
Entered ctor or method
Exiting ctor or method
Entered ctor or method
Exiting ctor or method
Entered ctor or method
Exiting ctor or method
Entered ctor or method
Exiting ctor or method
    

As this stands, the text to say we're entering or exiting isn't particularly useful. It doesn't convey the names of the classes or methods involved. Nor does it provide us any information about parameters passed, or values returned.

Although simplistic, what this does demonstrate is that, by defining the Tracing aspect, we are able to separate all common code related to tracing, from the main classes of Name and Address.

Let's just pause and repeat that - "we are able to separate all common code related to tracing from the main classes". Simply link this aspect with the original, simple, class code, and we get automatic tracing.

By making a small change to only the Tracing aspect more useful information about the objects and methods being traced is provided. So by changing the body of the before() and after() advices to use a further feature of AspectJ (thisJoinPoint) as shown in Listing 3, we get the output shown in Listing 4.

 package com.nigeleke.accuexample.aspects;
 public aspect Tracing {

   public pointcut constructor() :
	 execution(
	 com.nigeleke.accuexample.classes.*.new(..));

   public pointcut anyMethod() :
	 execution(
	 * com.nigeleke.accuexample.classes.*.*(..));

   before() : constructor() || anyMethod() {
	 System.out.println(
	   "Entered " + thisJoinPoint);
   }

   after() : constructor() || anyMethod() {
	 System.out.println(
	   "Exiting " + thisJoinPoint);
   }
 }
 
Listing 3

 Entered execution(com.nigeleke.accuexample.classes.Address(String, String))
 Exiting execution(com.nigeleke.accuexample.classes.Address(String, String))
 Entered execution(void com.nigeleke.accuexample.classes.Address.setStreet(String))
 Exiting execution(void com.nigeleke.accuexample.classes.Address.setStreet(String))
 Entered execution(void com.nigeleke.accuexample.classes.Address.setTown(String))
 Exiting execution(void com.nigeleke.accuexample.classes.Address.setTown(String))
 Entered execution(com.nigeleke.accuexample.classes.Name(String, String))
 Exiting execution(com.nigeleke.accuexample.classes.Name(String, String))
 Entered execution(void com.nigeleke.accuexample.classes.Name.setForename(String))
 Exiting execution(void com.nigeleke.accuexample.classes.Name.setForename(String))
 Entered execution(void com.nigeleke.accuexample.classes.Name.setSurname(String))
 Exiting execution(void com.nigeleke.accuexample.classes.Name.setSurname(String))
 
Listing 4

Already more useful output, and we have not had to touch any of the methods in the Name or Address classes.

Yet another small change to the aspect and we are able to list the argument values and the return values. In Listing 5, the around() advice is used so that we can access the return value, which gives the output in Listing 6.

 package com.nigeleke.accuexample.aspects;
 public aspect Tracing {
	public pointcut constructor() :
	 execution(
	  com.nigeleke.accuexample.classes.*.new(..));
	public pointcut anyMethod() :
	 execution(
	  * com.nigeleke.accuexample.classes.*.*(..));
   Object around() : constructor() || anyMethod() {
	 System.out.println(
	  "Entered " + thisJoinPoint.toShortString());
	  Object[] args = thisJoinPoint.getArgs();
	 for (Object arg : args) {
	   System.out.println(
		"with argument: " + arg);
	 }
	  Object o = proceed();
	  System.out.println(
	  "Exiting " + thisJoinPoint.toShortString());
	 System.out.println("returning: " + o);
	  return o;
   }
 }
 
Listing 5

 Entered execution(Address(..))
 with argument: aStreet
 with argument: aTown
 Exiting execution(Address(..))
 returning: null
 Entered execution(Address.setStreet(..))
 with argument: bStreet
 Exiting execution(Address.setStreet(..))
 returning: null
 Entered execution(Address.setTown(..))
 with argument: bTown
 Exiting execution(Address.setTown(..))
 returning: null
 Entered execution(Name(..))
 with argument: aForename
 with argument: aSurname
 Exiting execution(Name(..))
 returning: null
 Entered execution(Name.setForename(..))
 with argument: bForename
 Exiting execution(Name.setForename(..))
 returning: null
 Entered execution(Name.setSurname(..))
 with argument: bSurname
 Exiting execution(Name.setSurname(..))
 returning: null
 
Listing 6

Again we have not had to touch any of the methods in the Name or Address classes.

Further, when we add a new class, Account for example, the Tracing aspect displays even more power. The new class will automatically get the tracing functionality required for the package. The creator of the class need not even be aware that tracing is required, or have any knowledge about how tracing is performed. They can simply concentrate on the responsibilities of the Account class.

This simple example, therefore, shows the main concept introduced by aspect-oriented programming - cross-cutting concerns, i.e. common functionality across classes. The Tracing aspect manages common tracing functionality; the Name, Address and Account classes manage their sole responsibilities, and they do not need to be concerned with how to perform tracing in their methods. It also adds a consistency to what is traced, and how it is traced, which may otherwise be lost.

Visualising aspects

There is one immediate consequence of the Tracing aspect. It is not possible to know, simply by reading the Name and Address source code, that the code in Tracing has an impact on what gets executed.

This is not necessarily a bad thing. The writer of Name, Address and any other classes which get added to this package, should only be concerned with the simple responsibilities of these classes.

In some respects this is not much different to someone inspecting the code of some parent class in a hierarchy, without knowing, or needing to know, how the implementations of methods in the child classes override their parent.

In practise however, it is pragmatic to know what interactions occur between aspects and classes.

The AspectJ development environment [AJDT] provides us with two mechanisms to view the impact of aspects on other classes.

The first of these mechanisms is shown in figures 1 and 2, where there are small indicators in the left margin of the source code editor. The classes show inbound arrows, indicating where their methods are being advised by other aspects. The aspects show outbound arrows, indicating their advises will have a side effect on a class's methods.

Name.java
Figure 1

Name.java
Figure 2

The second mechanism provides a view of the bigger picture. Figure 3, taken from the space-war example provided with AJDT, shows a vertical bar for each class. The horizontal bars are colour coded with a different colour for each aspect. These show the approximate position within the class of the join-points affected by an advice. Double-clicking on them takes you to the appropriate position within the class source.

Name.java
Figure 3

Pointcuts

With the aspects previously defined we are able to see some of the power of aspect-oriented programming. The pointcut helps determine the join-point within the object-oriented code, which is affected by an aspect's advice.

pointcuts are not just used to determine simple execution paths, however. pointcut expressions can also be used to determine:

  1. calls made to any package, e.g. calls made to the standard java JDBC access methods.
  2. read or write access to a class's attributes.
  3. whether the execution path is within a given package.

In the first of these examples there is a distinction between the call of and the execution2 of a method. The join-point for a call exists just before the call to the method is made, i.e. in the body of the client. The join-point for execution exists just after the call has been made, but before the main body of the method is executed, i.e. in the body of the method.

Initially it appears preferable to use 'execution' rather than 'call' as the advice code will only get injected once. However there are times when only a library is available, and it is not possible for the advice code to be inserted into the library code. These are the times when 'call' is used.

In the final example, it is possible to have an aspect which restricts how output is performed and not allow calls via System.out. However the aspect itself may want to use System.out, so it becomes necessary to be able to state 'all calls to System.out that are not within this aspect's package'.

pointcut expressions can be very general, or very specific, depending on requirements. It is possible to define a pointcut expression, for example, for 'all public methods which start with set, take a String argument as the first parameter', for example this may be used to make sure no client sets null values.

Advices

What about advices? These are not restricted simply to injecting code before or after a method is executed. Firstly there is a more general use the before() and after() advices of our initial example. around() embraces both before() and after() advices. We used this earlier in the Tracing example, but is shown again in Listing 7 as a cut-down example.

 package com.nigeleke.accuexample.aspects;
 public aspect Tracing {

   public pointcut constructor() :
      execution(
        com.nigeleke.accuexample.classes.*.new(..));

  	public pointcut anyMethod() :
      execution(
  	    * com.nigeleke.accuexample.classes.*.*(..));

   Object around() : constructor() || anyMethod() {
  	 System.out.println("Entered ctor or method");
  	 Object o = proceed();
  	 System.out.println("Exiting ctor or method");
  	 return o;
   }
 }
 
Listing 7

Advices can also be used to declare compile time warnings and exceptions. This is something we alluded to earlier when we were discussing restricting calls via System.out.

The aspect in Listing 8 applies a policy on the usage of System.out, and creates a compile-time warning if System.out is used externally.

 package com.nigeleke.accuexample.aspects;
 public aspect OutputPolicy {

   pointcut accessSystemOut() :
	 get(* System.out);
	 pointcut inThisPackage() :
	 within(com.nigeleke.accuexample.aspects.*);
	declare warning :
	 accessSystemOut() && !inThisPackage() :
	 "Warning - System.out restrictions apply.";
 }
 
Listing 8

The warning can be changed to error when the policy needs to be enforced more strongly. When an error is declared then the developer's compilation will fail.

More advanced features of AOP

Given that this article started by looking at new programming concepts introduced in each generation of programming languages, I would like to address some of the more advanced features of AOP. Before I do though, I would also thoroughly recommend Eclipse AspectJ [Colyer et al] for a more detailed description. Although it is centred around the Java-based AspectJ language it still provides a very solid and easy to read explanation of AOP generally.

  • Abstract aspects. In the same way classes can be abstract and extended by concrete classes, so can aspects. We could, therefore, have an abstract Tracing aspect which defines pointcuts but the implementation of the advices are left to the concrete aspects. This way a framework, for example, can determine where tracing should be performed within the framework structure, but the implementation is required to implement the actual tracing, i.e. users define how.
  • Abstract pointcuts. Similarly pointcuts can be declared as being abstract, and the actual definition left for later. (Abstract pointcuts can only be declared within abstract aspects). If we look again at our framework tracing example - we may define the Tracing aspect so that its advices are implemented in the abstract aspect and provide the actual tracing wanted by the framework. The pointcut definitions can now be left to the concrete aspect, i.e. users of the framework determine what needs to be traced.
  • Method and field injection. An aspect can inject new methods and fields into existing classes. At first sight this may seem an odd and conflicting abstraction. It certainly does not sound like concerns are being kept separate.
Imagine, however, that we have the Address class, above and, as part of a user interface, we have an AddressUI class whose function is to display address objects. When the address object changes its observing addressUI object needs to be notified so that display fields are updated with the new values. This is a classic requirement for implementation of the Observer pattern [Gamma et al]. We can define (standard Java) Subject and Observer interfaces. We can define an abstract ObserverProtocol aspect that it will operate on objects providing Subject and Observer interfaces. The abstract ObserverProtocol aspect:
  • injects a field into Observers to hold the Subject being observed;
  • injects a method into Observers to set the subject;
  • injects a field into Subjects to hold the set of Observers;
  • injects methods into Subjects to add and remove an observer.
Finally we define a concrete AddressUiObserver whose responsibilities are:
  • to define Address as implementing Subject and AddressUI and implementing Observer.
  • define the concrete pointcut to state what events constitute an update on the Subject.
  • define the Subject's update() method as only this aspect knows what the Observer method needs to be called for notification of changes to the Subject.
That's it. The end result is a reusable observer protocol implementation and classes which know nothing of Subjects and Observers which are, nevertheless, able to provide Subject and Observer interfaces.

Finding and using aspects

When we write in object-oriented programming, the class names are generally nouns, e.g. Account, or Customer.

Good aspects come from other grammatical areas - adverbs and adjectives for example, e.g. size, speed. Real-life examples include security and auditing.

Temporal requirements also give an indication that an aspect may be appropriate, e.g. before performing this action the user must have been authenticated, or after writing to the database an audit record must be written.

Finally rule, or policy based requirements are also fine candidates for an aspect, e.g. loans over $10,000 must be approved by a lender.

There was3 a tendency among those new to object-oriented programming to create inappropriate classes, with unclear responsibilities or incorrect inheritance hierarchies. Further, I still observe that monolithic object-oriented spaghetti modules abound which are still a maintainer's nightmare. Perhaps we should call these spaghetti-meatballs?

Aspect-oriented programming is not the silver-bullet to fix this as it will still depend on the skill of the designer / programmer. I am certain that the use of AOP will still fall foul to environments that do not follow formal design practices, or well structured design reviews, or have personnel with a solid OO skill-set.

Also, just as our OO design practices have changed over the years and we have learned what constitutes good and bad OO design. I am sure those of us new to AOP will follow a few common bad practices and select aspects when they are not appropriate, or not use them when we could have done.

To help the learning curve for individuals, and for companies, the following adoption phases are recommended [AOSD]:

  • Explore AOP individually, or within small teams. Practical use would include enforcement policies, such as "do not use System.out" (as mentioned earlier). It is also possible enforcing package dependencies, to make certain these are not violated. These are compile time dependencies, so have no runtime impact. At this initial stage it is recommended to avoid runtime dependencies in a production environment.
  • Create project specific aspects and aspect libraries for the project infrastructure. This would include aspects like Tracing, which is a low-risk part of the project development.
  • Create core business aspects, i.e. those defining business rules for an individual application.
  • Define use within an AOP architecture, and introduce to all future development. This would include Security, Authorisation and Authentication aspects.

Not just Java

The examples given here are for AspectJ, which interleaves itself with Java classes, but there is also AspectC++ [AspectC++], which is very similar to AspectJ, but the advice bodies are C++, and the pointcut syntax aligns itself more closely with the C++ language.

The Spring framework [Spring (1)] also provides mechanisms to use aspect-oriented techniques. I believe this integrates closely with AspectJ, but also allows linking of aspects to the join-points via XML declarations or via the use of annotations. There are advantages and disadvantages in all styles and [Spring (2)] will lead you through these if you are interested.

I'm not really familiar with the Spring framework, but from a casual glance it appears that AspectJ provides a semantic richness, and a separation, not present in the other manners of defining aspects. [I would be more than happy to see an article on 'AOP with Spring']. It is the expressiveness permitted in AspectJ that enables the abstractions, mentioned at the start of this article, to be clearly articulated in code, so this would be my preference over using XML to express the same.

Miscellanea

One point that came to light while researching this article - Xerox have a US patent 6,467,086 for AOP/AspectJ. The AspectJ source code is released under the Common Public License, which grants some patent rights. And this all leads to a whole other discussion point regarding software patents.

Also, on the future of aspect-oriented programming, Bjarne Stroustrup was asked his opinion of aspect-oriented programming [InfoTech], where he stated: I don't see aspect-oriented programming escaping the "academic ghetto" any day soon, and if it does, it will be less pervasive than OO. When it works, aspect-oriented programming is elegant, but it's not clear how many applications significantly benefit from its use. Also, with AO, it appears difficult to integrate the necessary tools into an industrial-scale programming environment.

Personally I would really like to think he is wrong about AOP escaping the "academic ghetto", but I would certainly agree it requires the correct tools to be available. Although most of my experience has been with AspectJ, I will be following the development of AspectC++ with interest.

Summary

As you can see, programming languages have developed over the course of time so that we can communicate our ideas, our algorithms and our requirements more succinctly and more clearly to both the computer and the program maintainers.

Aspect-oriented programming languages provide a mechanism for the separation of the concerns, which were previously developed by common and repeated code snippets in object-oriented developments. I hope this article has given a small taste of aspect-oriented programming and what it can deliver for analysts, designers and developers.

One final thought from Feynman's biography: We must remove the rigidity of thought. ... we must leave freedom for the mind to wander about in try to solve the problems.

Hopefully aspect-oriented programming languages will enable us more freedom to solve our problems.

Acknowledgements

I would like to express my thanks to Paul Johnson, Alan Griffiths and everyone else on the Overload editorial team who critiqued and help improve my original submission.

References

[Gleick] James Gleick, Genius - Richard Feynman and Modern Physics, Abacus; ISBN 0-349-10532-4.

[Wiki(1)] http://en.wikipedia.org/wiki/Real_Programmers_Don't_Use_Pascal

[Wiki (2)] http://en.wikipedia.org/wiki/APL_language#Overview

[Wiki (3)] http://en.wikipedia.org/wiki/Alphabetical_list_of_programming_languages

[AJDT] AspectJ Development Tools (AJDT) - http://www.eclipse.org/ajdt/

[Colyer et al] Eclipse AspectJ; Adrian Colyer, Andy Clement, George Harley, Matthew Webster; Addison-Wesley; ISBN 0-321-24587-3.

[Gamma et al] Design Patterns; Gamma, Helm, Johnson and Vlissides; Addison-Wesley; ISBN 0-201-63361-2.

[AOSD] http://aosd.net/

[AspectC++] AspectC++ - http://acdt.aspectc.org/

[Spring (1)] Spring AOP - http://www.springframework.org/

[Spring (2)] Spring AOP declaration style - http://www.springframework.org/docs/reference/aop.html#aop-choosing

[InfoTech] http://www.techreview.com/InfoTech/17868/

Useful links

http://en.wikipedia.org/wiki/Programming_language

http://en.wikipedia.org/wiki/Structured_programming

http://en.wikipedia.org/wiki/Object-oriented_programming

Aspect-oriented Software Development - http://www.aosd.net/

US Patent 6,467,086 - Aspect Oriented Programming

1 Add the value '2' to the contents of register R1
2 Used earlier
3 Sad to say, still is...

Overload Journal #77 - Feb 2007 + Programming Topics + Design of applications and programs