« Fancy a weekend in Vancouver? (and learn about AOP too...) | Main | I've moved... »
June 04, 2004
Making Programs Simpler
This isn't what I originally intended to write about today - I was planning to write about Spring again, or about the crop of testimonials currently appearing on the AspectJ user's list - but there's an interesting debate going on 'back at the office' this afternoon and I thought I'd share some of that with you instead.
The central issue of the debate is whether aspect-oriented programming can make a meaningful contribution to tackling the complexities of software development, or whether the impact that can be made there is in fact relatively uninteresting in the grand scheme of things. To put in another way, do the inherent complexities in any real system of size dwarf into insignificance the complexity issues that the ideas of AOP can help address? Guess which side I'm arguing ;). Just to be clear, the other parties in the debate are AO supporters too, they just see the ideas of aspect orientation having an even more profound impact at the high-level design and architecture levels. I agree that AO can have a greater impact at the design and architecture levels (in the same way that I agree that any idea can have a greater impact in architecture than in design than in code simply because the implications of decisions made there are so much greater), but I still want aspect-oriented programming too.
So, is AOP useful or is it just fiddling around the edges of an already-too-complex set of programming languages and technologies? Let me put the case, and I'll leave you to make up your own mind (though I'm always interested to hear your opinions on the topic).
I want you to consider three programs, each program addresses the same requirement (so the inherent complexity in the problem statement is the same in every case), but the programs meet the requirement in different ways.
Here's the first program:
public class HelloWorld { static int[] char_data = new int[] {104,101,108,108,111,32,119,111,114,108,100}; public static void main(String[] args) { for (int i = 0; i < char_data.length; i++) { System.out.print((char)char_data[i]); } System.out.println(); } }
If you run this program you'll see the output:
hello world
Here's the second program:
public class HelloWorld { public static void main(String[] args) { System.out.println("hello world"); } }
If you run this program you'll see the output:
hello world
Finally, here's the third program:
print 'hello world'
If you run this program you'll see the output:
hello world
As a programmer, I think the second program is a significant improvement over the first program, and in turn the third program is a significant improvement over the second program. Moreover, I think those improvements do make a real difference to the ease of understanding, maintaining, and creating them.
But what on earth have these programs got to do with AOP???
When I wrote the first program, I put myself under the restriction that I couldn't use the abstraction 'String' (and nor 'character', as best I could). The difference between program 1 and program 2 illustrates the difference between the case when a programming language can directly support an abstraction useful to solving a problem and when it can't. Now think about the model-view-controller example again that I introduced in "AOP without the buzzwords." In plain Java, I can't directly represent the abstraction 'view notification,' so I have to code it long-hand with maybe a listener interface, some methods to add and remove listeners, and a bunch of code to notify listeners when state changes. In AspectJ, I can directly represent the abstraction ViewNotification. In fact, if I'm being pragmatic / agile / pick-your-favourite buzzword, I won't even bother with creating the full listener interface and registration machinery first time round - if i've just got a single model and a single view class, I'll simply encode the wiring directly. When you've got the ability to create a suitable abstraction, you can do things like that.
public aspect ViewNotification { private List Model.viewers = new ArrayList(); pointcut viewCreation(View v,Model m) : execution(View.new(Model)) && this(v) && args(m); pointcut viewDisposal(View v) : execution(* View.dispose(..)) && this(v); after(View v, Model m) returning : viewCreation(v,m) { m.viewers.add(v); } after(View v) returning : viewDisposal(v) { v.getModel().viewers.remove(v); // train wreck! } // one simple way of defining state change... pointcut modelStateChange(Model m) : set(!transient * Model.*) && target(m); after(Model m) returning : modelStateChange(m) { for (Iterator it = m.viewers.iterator(); it.hasNext(); ) { View v = (View) it.next(); v.modelUpdate(m); } } }
The ability to create (and even better to reuse from a library) meaningful abstractions is the difference between program 1 and program 2, and it's a difference that AOP can make for a non-trivial subset of problems.
I made a different kind of shift when I moved from the second hello world program to the third. (And no, the moral of the story is not 'just use perl!'). Instead of creating an appropriate abstraction, I changed my tool. I moved to a programming language (perl, but I bet the exact same source could be a valid program in a few other programming languages too) that allowed me to meet the requirement directly, without any baggage of 'stuff' there to keep the language happy, or to support the output statement. When you look at it this way, the Java program really does require a whole bunch of stuff that just gets in the way for solving this problem and adds no real value.
But what's that got to do with AOP either?
In "AOP without the buzzwords" I talked about concept:implementation ratios. The one ratio I didn't discuss was the one we see here (the article was getting too long already): a concept:implementation ration of 0:1. Is there such a thing? Sure there is - it happens when you get artefacts in the implementation that aren't there to directly represent any concept or idea in the design / requirements space (any time you deviate from 1:1 things start to get interesting). By creating directly usable abstractions, AOP can help us eliminate some of the implementation details that otherwise are there simply to support other pieces of the implementation. There's no requirement in the design that there be a listener interface, a registration mechanism, or an Observable model class. These are implementation artefacts we create to support the true requirement of view notification. In an AOP language, sometimes we can get away without those supporting constructs, and just write what we mean directly.
So, by adding abstraction and removing implementation clutter, we made a significant improvement from program 1 to program 3. AOP languages like AspectJ support both of those kinds of improvements in program structure too. I think the difference is worth it, and from the testimonials on the user lists, so do a number of others. What about you?
Posted by adrian at June 4, 2004 12:13 PM [permalink]
Comments
I gotta tell ya, the biggest problem I have with AOP is that I can't intuitively understand the code. By contrast, Java is an easy language to read (not necessarily write). All languages and tools make this trade-off; count me amongst those who prefer a bit more typing if it will make the code easier to learn/read/understand/maintain in the future. Granted, I'm admitting I've got a lot to learn here, but the AOP syntax is a big turn off.
Posted by: Anonymous at June 11, 2004 04:26 AM
Post a comment
Thanks for signing in, . Now you can comment. (sign out)
(If you haven't left a comment here before, you may need to be approved by the site owner before your comment will appear. Until then, it won't appear on the entry. Thanks for waiting.)