Skip to main content

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index] [List Home]
Re: "Humane Pointcut Languages" [Was: Re: [aspectj-users] AW:Pointcuton a constructor with a custom @Annotation]

I think I understand your approach.  You may recall the SDP/DIP were central
theoretical element of my 2004 AOSD tutorial, "Good AOP: Idioms, Rules, 
and Patterns in AspectJ": http://aosd.net/2004/tutorials/goodaop.php
I just don't think abstraction/specification captures the fragility of 
pointcuts, which I think stems from a program's join points changing.

Also missing from your account is consideration for complexity and
locality.  Your proposals displace concretization from the pointcut
to, say, an annotation that has to be put in the code.  Translating
"set..." to "@State" and "name" to "@Identifier" introduces indirection
and complexity without adding much.  Typically it is extrinsic factors --
e.g., that the annotation is already defined and used in the domain,
and has a well-understood meaning (and one hopes has tool support) --
that make using such indirection is a simplifying and/or clarifying operation.
Otherwise, we now have three pieces of code: the setter, the annotation,
and the aspect.  We've lost the locality we gained with aspects.  
You headed in that direction with your "pragmatic compromise."

I would love to see someone put forth a better pointcut language.  Perhaps
that would include some "interface" abstraction to normalize a set of 
pointcuts that is more effective than concretizing abstract aspects. So
your solutions might help.  I only disagreed with this statement of the problem:

> The problems we're facing with pointcuts are really
> examples of well-understood OOD principles. The fragile nature of
> pointcuts that refer to specific details is an example of breaking the
> Dependency Inversion Principle (DIP) 

That's a little like saying because my print() operation works for
my dot-matrix printer, it's "violating" DIP.

DIP is:
- high-level modules should not depend on low-level modules.
  both should depend upon abstractions.

- abstractions should not depend upon details. 
  details should depend upon abstractions

But any pointcut part (a method pattern or the annotation on the method) will
break if the use or intention of the method changes, or if the intention of
the pointcut shifts.  For example, setters could be renamed, there could be a
new naming pattern for setters, or state changes could include field-set.

So I guess I stand by my original statement: the fragile nature of pointcuts
is due to join points changing in form or meaning.  One of the key principles
behind what constitutes a join point is that it be stable under minor code
changes that do not affect semantics.  Note how weak this is.  If join points
were restricted to things that were more stable -- persisted under major
refactorings or use changes -- what would be a join point?  So there is a fairly
direct trade-off between power (more join points) and fragility (the join point 
model of a given program shifting as the program changes). 

AspectJ has some few design decisions to try to track change where it is well-
supported - notably the specification of the declaring type in code patterns.
This can make the pointcut robust to changes that should make no difference to the
programmer (e.g., lowering a method declaration in the type hierarchy), but it also
forms one of the main confusion points for new AspectJ programmers - understandability.

Abstraction/DIP is mainly about isolation: keeping a change in one place from
requiring changes in other places (see Meyer).  But in the case of pointcuts,
the changes in a program's join point model are in many places, and they need
to be tracked only in one place, the pointcut.  The difficulty is in knowing
when a change in the JP model is significant and requires some action in the 
pointcuts (and annotations, etc.).  Just as the original pointcut required an
understanding of the code for the target features, so will the possibly-changed
pointcut.

It's possible you could specify the target features - the concern - independently
and have the pointcut point to the concern and have the concern change as needed.
I'm not sure how this is different from, e.g., having components publish their
own pointcuts:

   public interface IPrint {
     public pointcut printing(): execution(void IPrint.print());
     public void print();
   }

This is the right kind of dependency inversion, possible in AspectJ, but it doesn't
address fragility: what if some other write() operation prints? what if some
IPrint.print() is used to pipeline document processing? what if some IPrint.print()
implementation delegates to toString(), which is otherwise accessed?  This is
fragility in join points: the intention changes, the code changes, and the intention
gets embedded in other code.  It does provide modularity, because we have one place

> ------------Original Message------------
> From: "Dean Wampler" <deanwampler@xxxxxxxxx>
> To: aspectj-users@xxxxxxxxxxx
> Date: Tue, May-2-2006 6:08 AM
> Subject: Re: "Humane Pointcut Languages" [Was: Re: [aspectj-users] AW:Pointcuton a constructor with a custom @Annotation]
>
> On 4/29/06, Wes <wes@xxxxxxxxxxxxxx> wrote:
> > > > The fragile nature of
> > > > pointcuts that refer to specific details is an example of 
> breaking
> > > the
> > > > Dependency Inversion Principle (DIP) and the Stable Dependencies
> > > > Principle (SDP),
> >
> > Sorry, I don't get it.  Why is concretizing an abstract pointcut any
> > different than concretizing an abstract class or method?  No one
> > expects less-abstract components to be more stable.
> 
> I didn't provide enough detail. Let me elaborate and show how these
> principles encapsulate ideas we've discussed in this thread.
> 
> Yes, creating concretizations of abstract pointcuts is very much like
> concretizing abstract classes and methods; done properly it is a good
> design practice. The problem, in both cases, is how we do it, in part
> because of the reasons you mention next about stability under
> evolution.
> 
> As an example, say I have an abstract aspect that supports the
> Subject/Observer pattern and I want to use it to watch for changes to
> the attribute "name" in class "Person". Typically, I might concretize
> an abstract pointcut that picks out the subject join points of
> interest, in this case something like "set(String Person.name)". I now
> have a dependency on details of a particular concrete class, which is
> inherently less stable, meaning these details are more likely to
> change, compared to interfaces or abstract classes. Our refactoring
> tooling isn't mature enough yet to catch all affected pointcuts
> (especially when wildcards are used). Of course, in a pure OO system,
> this kind of dependency is also fragile, as Martin discusses in the
> articles on the DIP and the SDP that I mentioned previously.
> 
> The SDP makes general statements about avoiding fragile dependencies
> in favor of more stable ones, with abstractions, like Java interfaces,
> tending to be more stable than concrete classes. (This also promotes
> more generic and hence reusable code, of course). The interesting
> thing about the DIP is that it "inverts" (the "I") responsibility for
> defining the interface on which a component depends. Take a typical
> layered architecture. Often a lower layer exposes interfaces for upper
> layers to use; the dependency points down. This sounds fine at first,
> but then the upper layers are now coupled to the lower-layer's idea of
> an interface, which may not be reusable with other possible lower
> layers. DIP says that instead the upper layer *should* define the
> interfaces for the services it needs and the lower layer(s) should
> implement those interface. More typically, the Bridge pattern is used
> to glue the two layers together; it implements the interfaces defined
> by the top layer using the interfaces exposed by the lower layers. If
> you picture the bridge as "floating" above and to the sides of the
> layers, then the dependencies from all layers to the bridge point up,
> inverting the dependency. (The diagrams in the article make it clear
> what I mean. I'm not doing justice to the description, either... ;) )
> 
> Back to the example aspect, instead of a concrete aspect that points
> directly to Person, I should define some sort of Subject "interface"
> that Person implements and my concrete observer should depend only on
> it. Let's start with an "interface" that's actually an annotation on
> the attributes, "@State", indicating which attributes hold state
> information (Jonas discusses the virtues of using annotations in his
> blog) . Now my pointcut is "set (@State * *.*)".  (First pass)
> 
> Now I'm decoupled from fragile, concrete details, going through an
> abstraction instead. However, now I will pick out all JPs that are
> state-related (and annotated as such). I might instead use several
> annotations that partition the information, where "name" is considered
> an "@Identifier", for example. However, I could get into tangling of
> concerns if I push my assumptions too far.
> 
> Another approach is to define a real interface that Person implements
> and the aspect advises. The true art for interface or annotation
> design is keeping them general enough to handle most needs, yet
> avoiding tangling concerns or assumptions about specific concerns into
> the code.
> 
> Finally, as a pragmatic compromise, I might define one abstract aspect
> that defines a pointcut like "set (@State String *.name)" and where
> the abstraction is in virtual methods called by the advice. So, *this*
> aspect is now a dependent of my more concrete "name" aspect, but it's
> still reusable by any other aspects interested in "names". I've kept
> the potentially-fragile coupling down to one location and the rest of
> the aspects and components remain loosely coupled and stable
> (hopefully).
> 
> Hope this makes more sense.
> 
> dean
> 
> > I think the problem is that join points might not be intended by
> > the "original" programmer as such, so they have no apriori stability 
> aside
> > from their history - structure and naming inferences.  (but see 
> annotations)
> > Nothing in a pointcut language can make up for a shifting join point 
> model;
> > indirection only displaces the work (though sometimes usefully back 
> to
> > to shifting programmer).
> >
> > As for more abstractions (like AO interface), I personally prefer 
> things
> > the programmer says in code, so we come to agreement on semantics and
> > support tool chains.
> >
> > Wes
> >
> 
> --
> Dean Wampler
> http://www.aspectprogramming.com
> http://www.newaspects.com
> http://www.contract4j.org
> _______________________________________________
> aspectj-users mailing list
> aspectj-users@xxxxxxxxxxx
> https://dev.eclipse.org/mailman/listinfo/aspectj-users
> 



Back to the top