There are either too many possible answers, or good answers would be too long for this format. Please add details to narrow the answer set or to isolate an issue that can be answered in a few paragraphs.
If this question can be reworded to fit the rules in the help center, please edit the question.

The visitor pattern can tackle a lot of situations that are now solved via AOP.
–
Steve EversNov 12 '10 at 3:44

@SnOrfus: Also see my response below, where I talk about the DJ library for Java: A dynamic way to use the visitor pattern! Worth checking out. (It's also a general technique you can use on your own with Reflection.)
–
MacneilNov 29 '10 at 4:01

5 Answers
5

When possible, you can encapsulate cross-cutting concerns into separate modules that are then used throughout the app via dependency injection. This allows you to somewhat decouple the cross-cutting concern implementation from it's use throughout the code.

This doesn't always work elegantly, though. That's the reason people are trying to address the issue with things like AOP.

In FP you represent a cross-cutting concern like anything else: as something you pass in on a function call. Since doing that explicitly gets tedious, you can use Monads (or maybe Arrows) to hide away the extra information being passed along.

The most common AOP example is logging. With Monads, you would create a "Logger" monad that keeps a list of messages. Any functions you perform through the LoggerMonad have the ability to post a log message. With Arrows, you would model the entire data flow of the application, and would work a logging routine into the model where appropriate. I think. Arrows are pretty complex.

Entity/Component-Based Programming

Something I've been researching and experimenting with for a game engine. Instead of "objects" like in OOP, you decompose everything into packets of data (components) and services that operate over a type of component. Components are grouped together by common IDs, like in a relational database, and groups of linked components are the Entities. To add logging in such a system, you would add a new logging service the triggers based on which components are passed through it.

Both methods allows for one to easily work a cross-cutting change in very easily, but both are high-level architectural models. So you would probably need to be using them from the beginning. The Component model can, theoretically, be worked into an existing OOP system. I guess monads could be too if your language is powerful enough.

There are several ways to tackle the problems of crosscutting concerns:

Use Better Design Patterns, Idioms, or Abstraction Mechanisms: Code may be crosscutting even though it can be modularized. In order to maintain the code, you will need to refactor to use the design technique that can modularize it. Such refactoring may introduce crosscutting of a different kind, but hopefully what crosscuts is stable, and not likely to change.

Develop Richer Language Features: Many manifestations of crosscutting can be solved through better abstraction mechanisms, and sometimes new language features are necessary. For instance, more advanced languages that include functional and object-oriented features often don't employ as many design patterns, because they aren't necessary. Note that design patterns themselves can be crosscutting in nature, because they describe the roles of several different objects and classes. In Java, reflection can often be used instead of an aspect, although at a higher runtime cost. For example, using reflection, you can support the visitor pattern over hundreds of classes with just a couple lines of code. The DJ library from Northeastern is one reflective solution that does just that. Mixins are a powerful technique available in C++ (but not Java) and can give you some of the same uses cases as an aspect.

Provide Better Tool Support: Techniques such as using grep and performing refactoring operations can deal with problems related to crosscutting code. For example, the name of a method declared in an interface can cut across the program. (Note the technical difference here: It is the name of the method, not the method's implementation, that crosscuts.) This usually isn't a problem in an IDE like Eclipse, where you can use the "rename refactoring" to change all of the places in your code that use the name. In this way, it's possible to not need language features when the programming environment is expressive enough for you.

Use Domain-Specific Languages: The early aspect languages, which came before AspectJ, were domain-specific and applied to only certain problems, such as thread synchronization or data-flow analysis for efficiently combining function compositions. These languages were experimental, but seemed highly successful at modularizing concerns that otherwise were crosscutting.

Use Generative Programming Techniques: Stepping up to the meta level might be considered an implementation technique for aspect-oriented programming, but it's a big enough area that it transcends simple aspects. Generative techniques (where a program generates source code for another program) are also related to domain-specific languages.

For all of these, I think studying AOP is appropriate. AOP can help you expand your conceptions of code, even if you don't use an AOP language.

AOP in its typical/classical sense requires a supporting tool (an aspect weaving IDE) to do it at large scale. AOP makes it harder to reason about code from the primary source code alone. Its harder to predict the behaviour of your components when your program is infiltrated by aspect zombies. Attributes or tags provide similar functionality but with explicit representation in the source code.
–
mumtazOct 26 '10 at 15:37

Note that my issue is not exactly with the problems that the AOP way solves. My only concern is that with AOP , my source code is not a sufficient source for predicting the behavior of my program.
–
mumtazOct 26 '10 at 15:44

@mumtaz: I can see how that would be the case when applying aspects to an entire namespace. The other method of AOP: attributing methods/properties/etc. to apply the aspect(s) to it, is identical to what you describe.
–
Steve EversOct 26 '10 at 19:06