Aspect Oriented Programming An Overview of AspectJ

advertisement
Aspect Oriented
Programming
An Overview of AspectJ
What is AOP?
• Not strictly OOP
• Not strictly POP
• Captures “aspects” that “crosscut”
both
– More on this later
A simple image filter (v1)
(defun or! (a b)
(let ((result (new-image)))
(loop for i from 1 to width do
(loop for j from 1 to height do
(set-pixel result i j
(or (get-pixel a i j)
(get-pixel b i j)))))
result))
• Other logical operations similar
• Shift image up, down similar
A simple image filter (v2)
(defun remove! (a b)
(and! a (not! b)))
Difference of two images
(defun top-edge! (a)
(remove! a (down! a)))
Pixels at top edge of region
(defun bottom-edge! (a)
(remove! a (up! a)))
Pixels at bottom edge of region
(defun horizontal-edge! (a)
(or! (top-edge! a)
(bottom-edge! a)))
Horizontal edge pixels
• Easy to understand, but not efficient
A simple image filter (v2)
(defun horizontal-edge-improved! (a)
(let ((result (new-image))
(a-up (up! a))
(a-down (down! a)))
(loop for i from 1 to width do
(loop for j from 1 to height do
(set-pixel result i j
(or (and (get-pixel a i j)
(not (get-pixel a-up i j)))
(and (get-pixel a i j)
(not (get-pixel a-down i j)))))))
result))
• Fewer image copies
• Reasoning less obvious
A simple image filter (v2)
Lessons learned
• Efficiency is good!
• Understanding is good!
• Can’t we have both?
good modularity
XML parsing
• XML parsing in org.apache.tomcat
– red shows relevant lines of code
– nicely fits in one box
good modularity
URL pattern matching
• URL pattern matching in org.apache.tomcat
– red shows relevant lines of code
– nicely fits in two boxes (using inheritance)
problems like…
logging is not modularized
• logging in org.apache.tomcat
– red shows lines of code that handle logging
– not in just one place
– not even in a small number of places
AspectJ
• Aspect Oriented environment for Java
• Developed at Xerox PARC
a simple figure editor
factory methods
Display
* FigureElement
Figure
makePoint(..)
makeLine(..)
Point
getX()
getY()
setX(int)
setY(int)
move(int, int)
move(int, int)
2
Line
getP1()
getP2()
setP1(Point)
setP2(Point)
move(int, int)
operations that
move elements
a simple figure editor
class Line implements FigureElement{
private Point p1, p2;
Point getP1() { return p1; }
Point getP2() { return p2; }
void setP1(Point p1) { this.p1 = p1; }
void setP2(Point p2) { this.p2 = p2; }
void moveBy(int dx, int dy) { ... }
}
class Point implements FigureElement {
private int x = 0, y = 0;
int getX() { return x; }
int getY() { return y; }
void setX(int x) { this.x = x; }
void setY(int y) { this.y = y; }
void moveBy(int dx, int dy) { ... }
}
join points
key points in dynamic call graph
imagine l.move(2, 2)
a Line
dispatch
a Point
a method execution
returning or throwing
a method call
returning or
throwing
a method execution
returning or throwing
dispatch
join point terminology
a Line
dispatch
method
execution
join points
• several kinds of join points
–
–
–
–
–
method & constructor call
method & constructor execution
field get & set
exception handler execution
static & dynamic initialization
method call
join points
primitive pointcuts
“a means of identifying join points”
a pointcut is a kind of predicate on join points that:
– can match or not match any given join point and
– optionally, can pull out some of the values at that join point
call(void Line.setP1(Point))
matches if the join point is a method call with this signature
pointcut composition
pointcuts compose like predicates, using &&, || and !
a “void Line.setP1(Point)” call
or
call(void Line.setP1(Point)) ||
call(void Line.setP2(Point));
a “void Line.setP2(Point)” call
whenever a Line receives a
“void setP1(Point)” or “void setP2(Point)” method call
user-defined pointcuts
defined using the pointcut construct
user-defined (aka named) pointcuts
– can be used in the same way as primitive pointcuts
pointcut move():
call(void Line.setP1(Point)) ||
call(void Line.setP2(Point));
after advice
action to take after
computation under join points
after advice runs
“on the way back out”
a Line
pointcut move():
call(void Line.setP1(Point)) ||
call(void Line.setP2(Point));
after() returning: move() {
<code here runs after each move>
}
a simple aspect
DisplayUpdating v1
an aspect defines a special class
that can crosscut other classes
aspect DisplayUpdating {
pointcut move():
call(void Line.setP1(Point)) ||
call(void Line.setP2(Point));
after() returning: move() {
Display.update();
}
}
box means complete running code
without AspectJ
DisplayUpdating v1
class Line {
private Point p1, p2;
Point getP1() { return p1; }
Point getP2() { return p2; }
void setP1(Point p1) {
this.p1 = p1;
Display.update();
}
void setP2(Point p2) {
this.p2 = p2;
Display.update();
}
}
• what you would expect
– update calls are tangled through the code
– “what is going on” is less explicit
pointcuts
can cut across multiple classes
pointcut move():
call(void Line.setP1(Point)) ||
call(void Line.setP2(Point)) ||
call(void Point.setX(int))
||
call(void Point.setY(int));
pointcuts
can use interface signatures
pointcut move():
call(void FigureElement.moveBy(int, int)) ||
call(void Line.setP1(Point))
||
call(void Line.setP2(Point))
||
call(void Point.setX(int))
||
call(void Point.setY(int));
a multi-class aspect
DisplayUpdating v2
aspect DisplayUpdating {
pointcut move():
call(void FigureElement.moveBy(int, int)) ||
call(void Line.setP1(Point))
||
call(void Line.setP2(Point))
||
call(void Point.setX(int))
||
call(void Point.setY(int));
after() returning: move() {
Display.update();
}
}
using values at join points
demonstrate first, explain in detail afterwards
• pointcut can explicitly expose certain values
• advice can use value
parameter
mechanism
being used
pointcut move(FigureElement figElt):
target(figElt) &&
(call(void FigureElement.moveBy(int, int)) ||
call(void Line.setP1(Point))
||
call(void Line.setP2(Point))
||
call(void Point.setX(int))
||
call(void Point.setY(int)));
after(FigureElement fe) returning: move(fe) {
<fe is bound to the figure element>
}
explaining parameters…
of user-defined pointcut designator
• variable is bound by user-defined pointcut declaration
– pointcut supplies value for variable
– value is available to all users of user-defined pointcut
pointcut parameters
pointcut move(Line l):
target(l) &&
(call(void Line.setP1(Point)) ||
call(void Line.setP2(Point)));
typed variable in place of type name
after(Line line): move(line) {
<line is bound to the line>
}
explaining parameters…
of advice
• variable is bound by advice declaration
– pointcut supplies value for variable
– value is available in advice body
pointcut move(Line l):
target(l) &&
(call(void Line.setP1(Point)) ||
call(void Line.setP2(Point)));
advice parameters
after(Line line): move(line) {
<line is bound to the line>
}
typed variable in place
of type name
context & multiple classes
DisplayUpdating v3
aspect DisplayUpdating {
pointcut move(FigureElement figElt):
target(figElt) &&
(call(void FigureElement.moveBy(int, int)) ||
call(void Line.setP1(Point))
||
call(void Line.setP2(Point))
||
call(void Point.setX(int))
||
call(void Point.setY(int)));
after(FigureElement fe): move(fe) {
Display.update(fe);
}
}
without AspectJ
class Line {
private Point p1, p2;
Point getP1() { return p1; }
Point getP2() { return p2; }
void setP1(Point p1) {
this.p1 = p1;
}
void setP2(Point p2) {
this.p2 = p2;
}
}
class Point
{
private int x = 0, y = 0;
int getX() { return x; }
int getY() { return y; }
void setX(int x) {
this.x = x;
}
void setY(int y) {
this.y = y;
}
}
without AspectJ
DisplayUpdating v1
class Line {
private Point p1, p2;
Point getP1() { return p1; }
Point getP2() { return p2; }
void setP1(Point p1) {
this.p1 = p1;
Display.update();
}
void setP2(Point p2) {
this.p2 = p2;
Display.update();
}
}
class Point
{
private int x = 0, y = 0;
int getX() { return x; }
int getY() { return y; }
void setX(int x) {
this.x = x;
}
void setY(int y) {
this.y = y;
}
}
without AspectJ
DisplayUpdating v2
class Line {
private Point p1, p2;
Point getP1() { return p1; }
Point getP2() { return p2; }
void setP1(Point p1) {
this.p1 = p1;
Display.update();
}
void setP2(Point p2) {
this.p2 = p2;
Display.update();
}
}
class Point
{
private int x = 0, y = 0;
int getX() { return x; }
int getY() { return y; }
void setX(int x) {
this.x = x;
Display.update();
}
void setY(int y) {
this.y = y;
Display.update();
}
}
without AspectJ
DisplayUpdating v3
class Line {
private Point p1, p2;
Point getP1() { return p1; }
Point getP2() { return p2; }
void setP1(Point p1) {
this.p1 = p1;
Display.update(this);
}
void setP2(Point p2) {
this.p2 = p2;
Display.update(this);
}
}
class Point
{
private int x = 0, y = 0;
int getX() { return x; }
int getY() { return y; }
void setX(int x) {
this.x = x;
Display.update(this);
}
void setY(int y) {
this.y = y;
Display.update(this);
}
}
• no locus of “display updating”
– evolution is cumbersome
– changes in all classes
– have to track & change all callers
with AspectJ
class Line {
private Point p1, p2;
Point getP1() { return p1; }
Point getP2() { return p2; }
void setP1(Point p1) {
this.p1 = p1;
}
void setP2(Point p2) {
this.p2 = p2;
}
}
class Point
{
private int x = 0, y = 0;
int getX() { return x; }
int getY() { return y; }
void setX(int x) {
this.x = x;
}
void setY(int y) {
this.y = y;
}
}
with AspectJ
DisplayUpdating v1
class Line {
aspect DisplayUpdating {
private Point p1, p2;
pointcut move():
call(void Line.setP1(Point)) ||
call(void Line.setP2(Point));
Point getP1() { return p1; }
Point getP2() { return p2; }
void setP1(Point p1) {
this.p1 = p1;
}
void setP2(Point p2) {
this.p2 = p2;
}
}
class Point
{
private int x = 0, y = 0;
int getX() { return x; }
int getY() { return y; }
void setX(int x) {
this.x = x;
}
void setY(int y) {
this.y = y;
}
}
after() returning: move() {
Display.update();
}
}
with AspectJ
DisplayUpdating v2
class Line {
aspect DisplayUpdating {
private Point p1, p2;
pointcut move():
call(void FigureElement.moveBy(int, int) ||
call(void Line.setP1(Point))
||
call(void Line.setP2(Point))
||
call(void Point.setX(int))
||
call(void Point.setY(int));
Point getP1() { return p1; }
Point getP2() { return p2; }
void setP1(Point p1) {
this.p1 = p1;
}
void setP2(Point p2) {
this.p2 = p2;
}
}
}
class Point
{
private int x = 0, y = 0;
int getX() { return x; }
int getY() { return y; }
void setX(int x) {
this.x = x;
}
void setY(int y) {
this.y = y;
}
}
after() returning: move() {
Display.update();
}
with AspectJ
DisplayUpdating v3
class Line {
aspect DisplayUpdating {
private Point p1, p2;
pointcut move(FigureElement figElt):
target(figElt) &&
(call(void FigureElement.moveBy(int, int) ||
call(void Line.setP1(Point))
||
call(void Line.setP2(Point))
||
call(void Point.setX(int))
||
call(void Point.setY(int)));
Point getP1() { return p1; }
Point getP2() { return p2; }
void setP1(Point p1) {
this.p1 = p1;
}
void setP2(Point p2) {
this.p2 = p2;
}
after(FigureElement fe) returning: move(fe) {
Display.Update(fe);
}
}
class Point
{
}
private int x = 0, y = 0;
int getX() { return x; }
int getY() { return y; }
void setX(int x) {
this.x = x;
}
void setY(int y) {
this.y = y;
}
}
• clear display updating module
– all changes in single aspect
– evolution is modular
aspects crosscut classes
aspect modularity cuts across
class modularity
Display
* FigureElement
Figure
makePoint(..)
makeLine(..)
Point
getX()
getY()
setX(int)
setY(int)
moveBy(int, int)
moveBy(int, int)
2
Line
getP1()
getP2()
setP1(Point)
setP2(Point)
moveBy(int, int)
DisplayUpdating
advice is
additional action to take at join points
• before
before proceeding at join point
• after returning a value to join point
• after throwing a throwable to join point
• after
returning to join point either way
• around
on arrival at join point gets explicit
control over when&if program proceeds
contract checking
simple example of before/after/around
• pre-conditions
– check whether parameter is valid
• post-conditions
– check whether values were set
• condition enforcement
– force parameters to be valid
pre-condition
using before advice
aspect PointBoundsPreCondition {
before(int newX):
call(void Point.setX(int)) && args(newX) {
assert(newX >= MIN_X);
what follows the ‘:’ is
assert(newX <= MAX_X);
always a pointcut –
}
primitive or user-defined
before(int newY):
call(void Point.setY(int)) && args(newY) {
assert(newY >= MIN_Y);
assert(newY <= MAX_Y);
}
private void assert(boolean v) {
if ( !v )
throw new RuntimeException();
}
}
post-condition
using after advice
aspect PointBoundsPostCondition {
after(Point p, int newX):
call(void Point.setX(int)) && target(p) && args(newX) {
assert(p.getX() == newX);
}
after(Point p, int newY):
call(void Point.setY(int)) && target(p) && args(newY) {
assert(p.getY() == newY);
}
private void assert(boolean v) {
if ( !v )
throw new RuntimeException();
}
}
condition enforcement
using around advice
aspect PointBoundsEnforcement {
void around(Point p, int newX):
call(void Point.setX(int)) && target(p) && args(newX) {
proceed(p, clip(newX, MIN_X, MAX_X));
}
void around(Point p, int newY):
call(void Point.setY(int)) && target(p) && args(newY) {
proceed(p, clip(newY, MIN_Y, MAX_Y));
}
private int clip(int val, int min, int max) {
return Math.max(min, Math.min(max, val));
}
}
wildcarding in pointcuts
target(Point)
target(graphics.geom.Point)
target(graphics.geom.*)
target(graphics..*)
“*” is wild card
“..” is multi-part wild card
any type in graphics.geom
any type in any sub-package
of graphics
call(void Point.setX(int))
call(public * Point.*(..))
call(public * *(..))
any public method on Point
any public method on any type
call(void
call(void
call(void
call(void
any getter
Point.getX())
Point.getY())
Point.get*())
get*())
call(Point.new(int, int))
call(new(..))
any constructor
role types and reusable
abstract aspect Observing {
protected interface Subject { }
protected interface Observer { }
public void
addObserver(Subject s, Observer o) { ... }
public void removeObserver(Subject s, Observer o) { ... }
abstract pointcut changes(Subject s);
after(Subject s): changes(s) {
Iterator iter = getObservers(s).iterator();
while ( iter.hasNext() ) {
notifyObserver(s, ((Observer)iter.next()));
}
}
abstract void notifyObserver(Subject s, Observer o);
}
this is the concrete reuse
DisplayUpdating v4
aspect DisplayUpdating extends Observing {
declare parents: FigureElement implements Subject;
declare parents: Display
implements Observer;
pointcut changes(Subject s):
call(void FigureElement.moveBy(int, int)) ||
call(void Line.setP1(Point))
||
call(void Line.setP2(Point))
||
call(void Point.setX(int))
||
call(void Point.setY(int));
void notifyObserver(Subject s, Observer o) {
((Display)o).needsRepaint();
}
}
other primitive pointcuts
cflow(pointcut designator)
all join points within the dynamic control flow of any join
point in pointcut designator
cflowbelow(pointcut designator)
all join points within the dynamic control flow below any join
point in pointcut designator
example 3
counting bytes
interface OutputStream {
public void write(byte
b);
public void write(byte[] b);
}
/**
* This SIMPLE aspect keeps a global count of all
* the bytes ever written to an OutputStream.
*/
aspect ByteCounting {
int count = 0;
int getCount() { return count; }
//
//
// what goes here? //
//
//
}
counting bytes v1
a first attempt
aspect ByteCounting {
int count = 0;
int getCount() { return count; }
after() returning:
call(void OutputStream.write(byte)) {
count = count + 1;
}
after(byte[] bytes) returning:
call(void OutputStream.write(bytes)) {
count = count + bytes.length;
}
}
counting bytes
some stream implementations
class SimpleOutputStream implements OutputStream {
public void write(byte b) { … }
public void write(byte[] b) {
for (int i = 0; i < b.length; i++) write(b[i]);
}
}
class OneOutputStream implements OutputStream {
public void write(byte b) { … }
public void write(byte[] b) { … }
}
counting bytes
another implementation
class OtherOutputStream implements OutputStream {
public void write(byte b) {
byte[] bs = new byte[1] { b };
write(bs);
}
public void write(byte[] b) { … }
}
counting bytes v2
using cflowbelow for more robust counting
aspect ByteCounting {
int count = 0;
int getCount() { return count; }
pointcut write(): call(void OutputStream.write(byte)) ||
call(void OutputStream.write(byte[]));
pointcut writeCflow(): cflowbelow(write());
after() returning:
!writeCflow() && call(void OutputStream .write(byte)) {
count++;
}
after(byte[] bytes) returning:
!writeCflow() && call(void OutputStream .write(bytes)) {
count = count + bytes.length;
}
}
AspectJ technology
• AspectJ is a small extension to Java™
– valid Java programs are also valid AspectJ programs
• AspectJ has its own compiler, ajc
– ajc runs on Java 2 platform (Java 1.2 – 1.4)
– ajc produces Java platform compatible .class files
• AspectJ tools support
– IDE extensions: Emacs, JBuilder 5, Forte4J
– ajdoc to parallel javadoc
– debugger: command line, GUI, & IDE
• license
– compiler, runtime and tools are free for any use
– compiler and tools are Open Source
when are aspects appropriate?
• is there a concern that:
– crosscuts the structure of several objects or operations
– is beneficial to separate out
… crosscutting
• a design concern that involves several
objects or operations
• implemented without AOP would lead
to distant places in the code that
– do the same thing
• e.g. traceEntry(“Point.set”)
• try grep to find these [Griswold]
– do a coordinated single thing
• e.g. timing, observer pattern
• harder to find these
… beneficial to separate out
• does it improve the code in real ways?
– separation of concerns
• e.g . think about service without timing
– clarifies interactions, reduces tangling
• e.g. all the traceEntry are really the same
– easier to modify / extend
• e.g. change the implementation of tracing
• e.g. abstract aspect re-use
Aspect Oriented
Programming
Integrating Independent Components with
On-Demand Remodularization
A generic tree display
interface TreeModel {
Object getRoot();
Object[] getChildren(Object node);
String getStringValue(Object node, boolean selected,
boolean expanded, boolean leaf, int row, boolean focus);
}
}
interface TreeGUIControl {
display();
}
class SimpleTreeDisplay implements TreeGUIControl {
TreeModel tm;
display() {
Object root = tm.getRoot();
... tm.getChildren(root) ...
... tm.getStringValue(...); ...
}
}
A generic tree display
class Expression {
Expression[] subExpressions;
String description() { ... }
Expression[] getSubExpressions() { ... }
}
class ExpressionDisplay implements TreeModel {
ExpressionDisplay(Expression r) { root = r; }
Expression root;
Object getRoot() { return root; }
Object[] getChildren(Object node) {
return ((Expression) node).getSubExpressions();
}
String getStringValue(Object node, boolean selected,
boolean expanded, boolean leaf, int row, boolean focus){
String s = ((Expression) node).description();
if (focus) s ="<"+s+">";
return s;
}
}
Problems with ExpressionDisplay
• Use of Object
– What can we reason about structure,
dataflow?
• Lose type safety
– Must rely on runtime checks
Problems with ExpressionDisplay
class Expression {
Expression[] subExpressions;
String description() { ... }
Expression[] getSubExpressions() { ... }
}
class ExpressionDisplay implements TreeModel {
ExpressionDisplay(Expression r) { root = r; }
Expression root;
Object getRoot() { return root; }
Object[] getChildren(Object node) {
return ((Expression) node).getSubExpressions();
}
String getStringValue(Object node, boolean selected,
boolean expanded, boolean leaf, int row, boolean focus){
String s = ((Expression) node).description();
if (focus) s ="<"+s+">";
return s;
}
}
Collaboration Interfaces
• Dual contracts
– Provided: analogous to normal component
exports
– Expected: “filled-in” by client for context
• Developer of component writes
Provided parts
– Assumes Expected parts to be filled in later
• Client must write Expected parts to
complete component
A generic tree display (CI)
interface TreeDisplay {
provided void display();
expected TreeNode getRoot();
interface TreeNode {
expected TreeNode[] getChildren();
expected String getStringValue();
provided display();
provided boolean isSelected(),
provided boolean isExpanded();
provided boolean isLeaf();
provided int row();
provided boolean hasFocus();
}
}
A generic tree display (CI)
interface TreeDisplay {
provided void display();
expected TreeNode getRoot();
interface TreeNode {
expected TreeNode[] getChildren();
expected String getStringValue();
provided display();
provided boolean isSelected(),
provided boolean isExpanded();
provided boolean isLeaf();
provided int row();
provided boolean hasFocus();
}
}
Implementer writes these
A generic tree display (CI)
interface TreeDisplay {
provided void display();
expected TreeNode getRoot();
interface TreeNode {
expected TreeNode[] getChildren();
expected String getStringValue();
provided display();
provided boolean isSelected(),
provided boolean isExpanded();
provided boolean isLeaf();
provided int row();
provided boolean hasFocus();
}
}
Binder writes these
Implementing the interface
class SimpleTreeDisplay implements TreeDisplay {
void onSelectionChange(TreeNode n, boolean selected) {
n.setSelected(true);
}
void display() {
getRoot().display();
}
class TreeNode {
boolean selected;
boolean isSelected() { return selected; }
void setSelected(boolean s) { selected =s;}
void display() {
... TreeNode c = getChildren()[i];
... paint(position, c.getStringValue()); ...
}
}
Called, but not defined yet
}
Binding the interface
class ExpressionDisplay binds TreeDisplay {
Expression root;
public ExpressionDisplay(Expression rootExpr) {
root = rootExpr;
}
TreeNode getRoot() {
return ExprTreeNode(root);
}
class ExprTreeNode binds TreeNode {
Expression e;
ExprTreeNode(Expression e) { this.e=e;}
TreeNode[] getChildren() {
return ExprTreeNode[](e.getSubExpressions());
}
String getStringValue() {
String s = e.description();
if (hasFocus()) s ="<"+s+">";
return s;
} } }
Instantiating the interface
• Neither class is sufficient by itself
• Create “compound” class that
combines both
• Compound class defined using “+”
class SimpleExpressionDisplay =
SimpleTreeDisplay + ExpressionDisplay;
...
class Plus extends Expression { … }
class Times extends Expression { … }
Expression test = new Plus(new Times(5, 3), 9);
TreeDisplay t = new SimpleExpressionDisplay(test);
t.display();
Wrapper recycling
• When returning nested objects, don’t
use new
– Avoid multiple “wrappers” for same object
• Wrapper provides 1-1 mapping to
embedded object
• Uses map for lookups
– 1st time, create new nested object
– Subsequent wrappings of same embedded
object return same nested object
Wrapper recycling
class ExpressionDisplay binds TreeDisplay {
Expression root;
public ExpressionDisplay(Expression rootExpr) {
root = rootExpr;
}
TreeNode getRoot() {
return ExprTreeNode(root);
}
class ExprTreeNode binds TreeNode {
Expression e;
ExprTreeNode(Expression e) { this.e=e;}
TreeNode[] getChildren() {
return ExprTreeNode[](e.getSubExpressions());
}
String getStringValue() {
String s = e.description();
if (hasFocus()) s ="<"+s+">";
return s;
} } }
Type safety
• Virtual types
– All nested interfaces, nested bindings, and
nested implementations are virtual
– Virtual types defined by enclosing instance
– Exposed nested objects are compound types
Type safety
Expression e = ...;
final ExpressionDisplay ed =
new SimpleExpressionDisplay(e);
...
// let FileSystemDisplay be a binding of
// TreeDisplay to the file system structure
SimpleFileSystemDisplay =
SimpleTreeDisplay + FileSystemDisplay;
FileSystem fs = ... ;
final FileSystemDisplay fsd =
new SimpleFileSystemDisplay(fs);
...
ed.TreeNode t = ed.getRoot();
fsd.setRoot(t); // Type error detected by typechecker!
// fsd.TreeNode is not subtype of ed.TreeNode
Type safety (polymorphism)
• We can still have polymorphism
• If C = A + B, then
– C<A
–C<B
Type safety (polymorphism)
class SucAugSched =
SuccessiveAugmentationColoring + SchedulingGraph;
class SimAnSched =
SimulatedAnnealingColoring + SchedulingGraph;
...
final SchedulingGraph sg =
wantSucAug ? new SucAugSched() : new SimAnSched();
sg.computeMinimumColoring( sg.CourseVertex[](courses) );
SucAugSched < SchedulingGraph and
SimAnSched < SchedulingGraph, so this is okay
Type safety (polymorphism)
• Multiple bindings of nested classes
allowed
• Only one implementation of nested
classes
• Constructors defined at binding site
only
• Asymmetry
Type safety (polymorphism)
class StudentKnowsTeacherGraph binds Graph {
class StudVertex binds Vertex {
final Student s;
StudVertex(Student s) {this.s=s;}
...
}
class TeacherVertex binds Vertex {
Teacher t;
TeacherVertex(Teacher t) { this.t = t; }
...
}
... Vertex v1 = StudVertex(aStudent); ...
... Vertex v2 = TeacherVertex(aTeacher); ...
}
Conclusions
• Is this AOP?
– In broad sense, maybe (not exactly OOP or
POP)
– Not typical crosscutting
• Similar to Jiazzi
– “mixins” done at finer grain (procedure level)
• Could we solve with templates
(generics)?
Conclusions
• Do we buy it?
– Adds type safety
– Wrapper recycling not terribly exciting
Download