OOP Metaprogramming Series: Introduction to Java Aspect-Oriented Programming
In my last post, we covered a general idea of crosscutting, a phenomenon where two separate concerns (aspects) of a program are composed differently but must coordinate with one another. Separate compositional aspects being forced to work in tandem with each other can result in tangling and scattering: in other words, a lot of WTF code. A lot of your boss riding you extra hard because debugging an essential feature was set up in a class thousands of lines long and you’ve been sifting through for a couple hours now trying to figure out why your program can’t keep track traversing a file tree. Part of it could be that the code is badly written. Part of it could be misguided design decisions. Code that is difficult to debug and maintain can be symptomatic of crosscutting, the invisible force that twists your arm on making secondary decisions to fit other aspects.
In the industry, you may find yourself sacrificing one concern for another while working with an object-oriented paradigm. A bit of code written concisely may have straightforward-logic, but it’s memory usage may be so poor that paying for physical storage becomes a serious expense for your company. Worst case scenario, your program is taking seconds, SECONDS to initialize. Whether it be a forum or a CRM that uses analytics for real-time actionable suggestions, we never stop worrying about how our aspects interact on an independent basis.
The conception of Aspect-Oriented Programming (AOP) is a result of Object Oriented Programming’s (OOP) attempts to grapple creating cohesive aspect-relations. AOP is the marriage of functional thinking and OOP, aimed to achieve some level of computational reflection through metaobjects, which gives control of the underlying structures/behaviors of a program.
A generalized-procedure language generally has properties that can be composed by:
- A component, if it can be cleanly encapsulated in a generalized procedure that is localized, easily accessible and composed, or
- An aspect, if it cannot be cleanly encapsulated in a generalized procedure
What is an Aspect?
Aspects tend to not be units of a system’s functional decomposition, but rather properties that affect performance or semantics of the components in systematic ways. The base unit of modularity in OOP is a class, whereas in AOP the unit of modularity is the aspect.
Aspects are essentially a separation of concerns, and the aim of AOP is to be able to cleanly separate these concerns cleanly by supplying an aspect weaver. To create an abstract that serves as a mechanism to compose aspects, a AOP implementation may produce an overall system with the following:
- a component language that serves the sole role of programming components
- an aspect weaver with which to program the aspects
- A component program that implements the aspects using the aspect languages
Components contain filters that are no longer explicitly procedures and primitive loops are written in a way that makes their loop structure as explicit as possible. An aspect language is a procedural language that provides simple operations on the elements with existing relations. Let’s revisit the issue mentioned in the previous post: we examined an attempted rewrite of the pixel manipulation example provided by Kizcales’ Aspect-Oriented Programming . Java’s most natural uses of metalevel concepts risk crosscutting. The original function manipulated the pixels of a black-and-white image, starting with a function that created a new image at the end of the loop.
Talk about costly. Concerned with memory usage optimization, we create a function whose loops became difficult to read and more costly over time because of increased hours required to to maintain and debug. In this particular example, apects are defined between data flow and logic. Because they are not cohesive, we find ourselves sacrificing one domain for another in either case.
One benefit of an aspect weaver is its discriminative selections of features it wishes to concern itself with, reducing the overhead that tends to be associated with full-reflection. Aspect weavers may be composed by a model in which there is:
- the chosen program elements of interest
- the action associated with these program elements
- an explicit entity that may link these 2 aspects together
On reexamining the rewritten feature, the source of difficulty in trying to create a function that is both readable and memory-efficient seems to lie in the way we write our filters.
Rewriting this filter as a higher-order function achieves the goal of writing a component language, where loops are now as explicit as possible and filtering is no longer written as part of the function. Observing the use of an aspect-weaver with diagrams allows us to realize a better cohesive coexistence between data-flow and logic composition. In comparison to the diagrams observed previously, the analysis of the two diagrams as one is easier to process while we are able to successfully achieve both domain designs.
Current Shortcomings for AOP
That Java’s features restrict metaprogramming capabilities is a tough pill to swallow. If Java were to attempt indiscriminate reflexive programming — code that modifies itself at runtime — strict static types will always be required when creating new classes and properties. Once fields/methods are created, the type, and oftentimes the value, is immutable. Reflection involves dynamic type resolving, so Java Virtual machine optimizations cannot be performed, making reflection slower.
Reflection allows code to perform operations that cannot be done by Java such as accessing private fields and methods. As a result, it may not be possible to grant access for reflection, which may well render dysfunctional code. True reflection has a penchant for exposing members of a class to another, which creates conflict between abstraction and type reflection.
As my favorite professor once said, computers are stupid and malicious. Metaprogramming stops short because Java AOP has no way of maintaining access control. Unable to respect encapsulation, reflective code risks breaking abstractions that would change the overall behavior of code.
What does this mean exactly? For starters, we have a lot of work ahead of us. It will be extremely difficult to overcome these challenges with the current existing models of reflection. Maybe the idea of metaprogramming is so meta and cold and intimidating that maybe we need metaprogramming to be a welcoming space for human considerations like boundaries. Maybe we need to find a more compatible space for functional thinking and OOP. Maybe we ought to create an aspect weaver between reflection and OOP (meta-metaprogramming??). Ideally, metaprogramming ought to be able to be consistent with the same language it works with, compile-time objects ought to be first-class citizens, and a language that supports metaprogramming should be easier than today’s modern programming languages. The attempt for creating a rational framework for metaprogramming is certainly a tall order, but perhaps it is time for us to head this challenge.