Do we need Aspect-oriented programming?

Kristóf Répás
7 min readJan 9, 2020

--

SUPPORTED BY THE ÚNKP-19–1 NEW NATIONAL EXCELLENCE PROGRAM OF THE MINISTRY FOR INNOVATION AND TECHNOLOGY.

For the English version click here.

Like the history of art, every era of software development has its own dominant paradigm. Paradigm shifts are not happening in concrete time and place, instead they exist simultaneously. The new paradigms try to keep its predecessor’s advantages and solve their disadvantages and limitations.

Believe or not, Object-oriented programming paradigm (OOP) also has its own limitations. We will discuss here about Aspect-oriented programming paradigm (AOP), which tries to neutralize some of OOP’s disadvantages and solves its limitations by using aspects.

Aspect is such as a class, which can contain methods, fields, and it can be abstract. An aspect can affect multiple classes in the business logic.

AOP is based on Separation of Concerns (SoC), which means related features must be separated into manageable parts and these parts must be responsible for different concerns in the program.

SoC states, that if two concerns, like components, classes, etc., can be separated, then it worth separating them. For example the Builder design pattern separates the construction of a complex object from its representation. (1)

The idea behind AOP is to form a new abstraction level above OOP to handle those things we need almost everywhere in the business logic. Using AOP you can pull out these features from the business logic and encapsulate them in one transparent place.

AOP is an independent paradigm, but in fact, it must be evaulated beside OOP.

Simple Example

Imagine, you have created a beautiful object-oriented program. This simple application offers the ability of customizing and ordering a bag of tea. Beyond this, your program should be able to use logging, when the user customizes the desired type of tea. Your log implementation is the following in the business logic (fields and properties are not shown):

import java.util.logging.Logger;
import java.util.logging.Level;

public class Tea {

Logger logger = Logger.getLogger(Tea.class.getName());

public Tea(String name, String taste, int packet) {
this.setName(name);
this.setTaste(taste);
this.setPacket(packet);
logger.log(Level.INFO, “The user customized tea pocket!”);
}

public void order(Tea tea) {
System.out.println(“You ordered “ + tea.getPacket()
+ “ packet “ + tea.getName() + “ “ + tea.getTaste() + “!”);
}
}

It works properly, but what if you would like to log when the user places an order, too? You should implement a roughly same code here:

public void order(Tea tea) {
System.out.println(“You ordered “ + tea.getPacket()
+ “ packet “ + tea.getName() + “ “ + tea.getTaste() + “!”);
logger.log(Level.INFO, “The user ordered tea pocket(s)!”);
}

And so on, if you would like to log every steps of the users you have to implement it in every place where you would like to log.

Another problem with this is that the business logic and the logging part overlap. The logging feature messes up the business logic, which is contrary to two SOLID principle, the Single Responsibility Principle (SRP) and the Open/Closed Principle (OCP).

SRP

According to the SRP

we have to make sure that our classes have only one responsibility. […] If a class has more than one responsibility, (1)

then we have to change it more frequently than necessary.

If a class has less than one responsibility, then two or more classes share the same responsibility, (1)

so we have to change more classes at the same time than necessary.

These overlapping features do exactly that, and violate the SRP from both sides: the class has more than one responsibility, and two or more classes share the same responsibility, too.

For example besides the essential responsibility, your Tea, Beer and Wine classes implement and share the responsibility of logging.

It is strange that OOP is about encapsulation, and now you have a feature that is required in several classes, so you have to implement it more than once. Do you really have to?
Actually no, you do not have to.

AOP complements OOP

AOP is about how to solve OOP design problems like that one above. In fact, AOP complements OOP by using SoC, improving modularization.

When you are designing based on OOP, you focus on data and data-related operations and encapsulating them into classes. During this process you may ignore some other important design opportunities, so it is possible that essential features or non-essential features will be scattered throughout your code. (See the Simple Example section, where we violated the SRP.) This means, it will be difficult to debug, test and refactor these parts of the code.

Features like that called cross-cutting features. Best examples are logging, synchronization, monitoring, authenticating, puffering, transaction — management and context-dependent error handling. (2)

When you are designing based on AOP, you focus on related tasks and features. You do not have to deal with data and data-related operations, because you has done it earlier with OOP designing.

It also means, you can not design a system from the first day based on AOP, but you do not have to either.

Since AOP cooperates with OOP you can implement the program using OOP first. Second, you should deal with cross-cutting features and implement the aspects. Finally, the executable program is a combination of OOP code and the aspects. (2)

Using AOP, objects can have context-dependent behavior. This is not possible in OOP, because it is about encapsulation and not about separating implementation of tasks. OOP’s target is to save the objects from their environment’s impact, in order to keep the objects under our control.

AOP allows objects to have context-dependent behavior without losing our control, thereby you can implement the context-dependent code inside an aspect.

OCP

Code made with OOP does not have to know anything about the aspects. It also means, you do not have to change anything in the original code, when you add the aspects to it. (2)
Because of this, AOP designing and adding aspects are consistent with Open/Closed Principle (OCP).

OCP states that

our codebase should be open for extension but closed for modification. (1)

For example, if you would like to add the logging feature to the business logic, you may need to insert it into an existing method, which contrary to OCP, because you modify an existing code. Instead, you should create the aspects in a new structure and implement the new feature there.

Problems with cross-cutting features in OOP

  1. You have to implement them everywhere you need them.
  2. In some cases, they mess up the responsibilities of the class in the business logic. It is contrary to SRP.
  3. They will be scattered throughout the code. It is contrary to SRP.
  4. When you insert them into the code it can be contrary to OCP.

AOP solves these by using aspects. If you are using AOP:

  1. You can develop a more transparent code.
  2. Cross-cutting features will be encapsulated instead of being scattered. It is consistent with SRP.
  3. You do not have to change anything in the original code. It is consistent with OCP.
  4. You can add aspects anytime. It is consistent with OCP.

Using AOP you can encapsulate essential and non-essential parts into its own class, called aspect. This means, the business logic can stay pure with its responsibilities and it does not need to implement any subsidiary parts such as logging.

Refactoring the Simple Example

Check this article to the AOP terminology.

The business logic part is the following:

public class Tea {
public Tea(String name, String taste, int packet) {
this.setName(name);
this.setTaste(taste);
this.setPacket(packet);
}

public void order(Tea tea) {
System.out.println(“You ordered “ + tea.getPacket()
+ “ packet “ + tea.getName() + “ “
+ tea.getTaste() + “!”);
}
}

You can see, there is nothing here about logging, but pure business logic. Besides, the code has not changed compared to the original one at all.

The logging part is the following:

import java.util.logging.Logger;
import java.util.logging.Level;
public aspect LogAspect {

Logger logger = Logger.getLogger(Tea.class.getName());

pointcut customizeTeaLog():
call(Tea.new(String, String, int));

after(): customizeTeaLog() {
logger.log(Level.INFO, “The user customized tea pocket!”);
}

pointcut orderTeaLog(): call(void order(*));

after(): orderTeaLog() {
logger.log(Level.INFO, “The user ordered tea pocket(s)!”);
}
}

As I mentioned earlier, the business logic does not have to know anything about the aspect, it still has to work with and without it. Using aspect we managed to separate the logging cross-cutting feature from the business logic, in addition to encapsulate it. Wherever the Tea constructor or the order() method is called, the appropriate advice in the aspect is going to work.

Summary

Achievements of using AOP:

  1. It results more transparent and clean source code.
  2. It separates the non-essential from the essential and the subsidiary features from the business logic.
  3. It allows objects to have context-dependent behavior without losing our control.
  4. It opens new perspectives in system design.
  5. It improves the presence of SRP and OCP in the source code.
  6. It simplifies the management of cross-cutting features.
  7. It can be applied without changing anything in the existing codebase.
  8. The business logic can work with and without it.

The only difficulty is mastering the syntax and thinking of a new level of abstraction. But if you know the OOP, you can easily recognize the cross-cutting features, to which the AOP can be applied.

The important thing is that AOP will become a useful member of your toolbox, when OOP struggles with a particular task, in this case on cross-cutting features. AOP will not replace OOP, only complement it.

Bibliography

(1) Danisovszky, M., Nagy, T., Répás, K., Kusper, G.: Western Canon of Software Engineering: The Abstract Principles, 2019

(2) Lengyel, L., Levendovszky, T.: Aspektus-orientált programozás (Aspect-oriented programming), Híradástechnika Vol. \ LIX (2004/10, 8–12)

--

--

No responses yet