Guillaume Massé
Guillaume Massé

Reputation: 8064

Is is possible to produce a generic aspect?

for example this observer pattern

https://github.com/eclipse/org.aspectj/blob/master/docs/sandbox/ubc-design-patterns/src/ca/ubc/cs/spl/aspectPatterns/patternLibrary/ObserverProtocol.java

could be written like so

public abstract aspect ObserverProtocol<S implements Subject, O implements Observer> {
  // ...
  protected abstract pointcut subjectChange(S s);
  protected abstract void updateObserver(S subject, O observer);
}

Upvotes: 0

Views: 596

Answers (1)

kriegaex
kriegaex

Reputation: 67327

Yes, generic abstract aspects are possible, see AspectJ 5 Development Kit Developer's Notebook. You will find an instructive example there. Give me a sign if you need more help.


Update as a reaction to your question below: Well, in this case I do not see much value in using generics for the Observer pattern, but it is possible and requires some refactoring. I cloned the repo and refactored the code a little. Now it reads like this (sorry, this is going to be lengthy!):

package ca.ubc.cs.spl.aspectPatterns.patternLibrary;

import java.util.WeakHashMap;
import java.util.List;
import java.util.LinkedList;
import java.util.Iterator;

public abstract aspect ObserverProtocol<S extends Subject, O extends Observer> {
    private WeakHashMap<S, List<O>> perSubjectObservers =
        new WeakHashMap<S, List<O>>();

    protected List<O> getObservers(S subject) {
        List<O> observers = perSubjectObservers.get(subject);
        if (observers == null) {
            observers = new LinkedList<O>();
            perSubjectObservers.put(subject, observers);
        }
        return observers;
    }

    public void addObserver(S subject, O observer) {
        getObservers(subject).add(observer);
    }

    public void removeObserver(S subject, O observer) {
        getObservers(subject).remove(observer);
    }

    protected abstract pointcut subjectChange(S s);

    after(S subject): subjectChange(subject) {
        Iterator<O> iter = getObservers(subject).iterator();
        while (iter.hasNext())
            updateObserver(subject, iter.next());
    }

    protected abstract void updateObserver(S subject, O observer);
}
package ca.ubc.cs.spl.aspectPatterns.patternLibrary;

public interface Subject {}
package ca.ubc.cs.spl.aspectPatterns.patternLibrary;

public interface Observer {}
package ca.ubc.cs.spl.aspectPatterns.examples.observer.aspectj;

import java.awt.Color;

public class Point {
    private int x;
    private int y;
    private Color color;

    public Point(int x, int y, Color color) {
        this.x = x;
        this.y = y;
        this.color = color;
    }

    public int getX() { return x; }
    public int getY() { return y; }
    public void setX(int x) { this.x = x; }
    public void setY(int y) { this.y = y; }
    public Color getColor() { return color; }
    public void setColor(Color color) { this.color = color; }
}
package ca.ubc.cs.spl.aspectPatterns.examples.observer.aspectj;

public class Screen {
    private String name;

    public Screen(String s) {
        this.name = s;
    }

    public void display(String s) {
        System.out.println(name + ": " + s);
    }
}
package ca.ubc.cs.spl.aspectPatterns.examples.observer.aspectj;

import ca.ubc.cs.spl.aspectPatterns.patternLibrary.Subject;
import ca.ubc.cs.spl.aspectPatterns.patternLibrary.Observer;

public aspect SubjectObserverDeclarations {
    declare parents: Point  implements Subject;
    declare parents: Screen implements Observer;
    declare parents: Screen implements Subject;
}
package ca.ubc.cs.spl.aspectPatterns.examples.observer.aspectj;

import java.awt.Color;
import ca.ubc.cs.spl.aspectPatterns.patternLibrary.ObserverProtocol;

public aspect ColorObserver extends ObserverProtocol<Point, Screen> {
    declare precedence : SubjectObserverDeclarations, ColorObserver;

    protected pointcut subjectChange(Point subject):
        call(void Point.setColor(Color)) && target(subject);

    protected void updateObserver(Point subject, Screen observer) {
        observer.display("screen updated (point subject changed color)");
    }
}
package ca.ubc.cs.spl.aspectPatterns.examples.observer.aspectj;

import ca.ubc.cs.spl.aspectPatterns.patternLibrary.ObserverProtocol;

public aspect CoordinateObserver extends ObserverProtocol<Point, Screen>{
    declare precedence : SubjectObserverDeclarations, CoordinateObserver;

    protected pointcut subjectChange(Point subject):
        (call(void Point.setX(int)) || call(void Point.setY(int))) && target(subject);

    protected void updateObserver(Point subject, Screen observer) {
        observer.display("screen updated (point subject changed coordinates)");
    }
}
package ca.ubc.cs.spl.aspectPatterns.examples.observer.aspectj;

import ca.ubc.cs.spl.aspectPatterns.patternLibrary.ObserverProtocol;

public aspect ScreenObserver extends ObserverProtocol<Screen, Screen>{
    declare precedence : SubjectObserverDeclarations, ScreenObserver;

    protected pointcut subjectChange(Screen subject):
        call(void Screen.display(String)) && target(subject);

    protected void updateObserver(Screen subject, Screen observer) {
        observer.display("screen updated (screen subject displayed message)");
    }
}
package ca.ubc.cs.spl.aspectPatterns.examples.observer.aspectj;

import java.awt.Color;

public class Main {
    public static void main(String argv[]) {
        System.out.println("Creating screens s1, s2, s3, s4, s5 and point p");
        Point p = new Point(5, 5, Color.blue);
        Screen s1 = new Screen("s1");
        Screen s2 = new Screen("s2");
        Screen s3 = new Screen("s3");
        Screen s4 = new Screen("s4");
        Screen s5 = new Screen("s5");

        System.out.println("Creating observing relationships:");
        System.out.println("- s1 and s2 observe color changes to p");
        System.out.println("- s3 and s4 observe coordinate changes to p");
        System.out.println("- s5 observes s2's and s4's display() method");

        ColorObserver.aspectOf().addObserver(p, s1);
        ColorObserver.aspectOf().addObserver(p, s2);

        CoordinateObserver.aspectOf().addObserver(p, s3);
        CoordinateObserver.aspectOf().addObserver(p, s4);

        ScreenObserver.aspectOf().addObserver(s2, s5);
        ScreenObserver.aspectOf().addObserver(s4, s5);

        System.out.println("Changing p's color:");
        p.setColor(Color.red);

        System.out.println("Changing p's x-coordinate:");
        p.setX(4);

        System.out.println("done.");
    }
}

As you can see, the abstract base aspect ObserverProtocol now uses generics, but the price is that we now need aspect SubjectObserverDeclarations for our declare parents statements, otherwise the derived aspects could not use the classes because if we declared the parents inside them it would be too late for the derived aspects to compile correctly (hen-and-egg problem).

You also see that consequently pointcut subjectChange now binds its target to concrete classes, no longer to the interface Subject. Similarly, method updateObserver also uses classes rather than interfaces.

For simplicity's sake, I extracted the interfaces Subject and Observer from the base aspect to avoid an aspect declaration like:

public abstract aspect ObserverProtocol<
    S extends ObserverProtocol.Subject,
    O extends ObserverProtocol.Observer>
{
    //...
}

Upvotes: 1

Related Questions