Reputation: 8064
for example this observer pattern
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
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