Typically, an aspect is
scattered or
tangled as code, making it harder to understand and maintain. It is scattered by the function (such as logging) being spread over a number of unrelated functions that might use
its function, possibly in entirely unrelated systems or written in different languages. Thus, changing logging can require modifying all affected modules. Aspects become tangled not only with the mainline function of the systems in which they are expressed but also with each other. Changing one concern thus entails understanding all the tangled concerns or having some means by which the effect of changes can be inferred. For example, consider a banking application with a conceptually very simple method for transferring an amount from one account to another. Such an example in
Java looks like: sealed class BankingException extends Exception permits InsufficientFundsException, UnauthorisedUserException { // ... } public class Bank { public void transfer(Account fromAcc, Account toAcc, int amount) throws BankingException { if (fromAcc.getBalance() However, this transfer method overlooks certain considerations that a deployed application would require, such as verifying that the current user is authorized to perform this operation, encapsulating
database transactions to prevent accidental data loss, and logging the operation for diagnostic purposes. A version with all those new concerns might look like this: import java.util.logging.*; sealed class BankingException extends Exception permits InsufficientFundsException, UnauthorisedUserException { // ... } public class Bank { private static final Logger logger; private final Database database; public void transfer(Account fromAcc, Account toAcc, int amount, User user) throws BankingException { logger.info("Transferring money..."); if (!isUserAuthorised(user, fromAcc)) { logger.log(Level.WARNING, "User has no permission."); throw new UnauthorisedUserException(); } if (fromAcc.getBalance() In this example, other interests have become
tangled with the basic functionality (sometimes called the
business logic concern). Transactions, security, and logging all exemplify
cross-cutting concerns. Now consider what would happen if we suddenly need to change the security considerations for the application. In the program's current version, security-related operations appear
scattered across numerous methods, and such a change would require major effort. AOP tries to solve this problem by allowing the programmer to express cross-cutting concerns in stand-alone modules called
aspects. Aspects can contain
advice (code joined to specified points in the program) and
inter-type declarations (structural members added to other classes). For example, a security module can include advice that performs a security check before accessing a bank account. The
pointcut defines the times (
join points) when one can access a bank account, and the code in the advice body defines how the security check is implemented. That way, both the check and the places can be maintained in one place. Further, a good pointcut can anticipate later program changes, so if another developer creates a new method to access the bank account, the advice will apply to the new method when it executes. So for the example above implementing logging in an aspect: aspect Logger { Logger logger; void Bank.transfer(Account fromAcc, Account toAcc, int amount, User user) { logger.info("Transferring money..."); } void Bank.getMoneyBack(User user, int transactionId) { logger.info("User requested money back."); } // Other crosscutting code. } One can think of AOP as a debugging tool or a user-level tool. Advice should be reserved for cases in which one cannot get the function changed (user level) or do not want to change the function in production code (debugging). ==Join point models==