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]

On 5/2/06, Wes <wes@xxxxxxxxxxxxxx> wrote:
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 didn't know that. Too bad I didn't attend that one ;)

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."

It's certainly true that you should only take steps like these if the
benefits outweigh the extra "indirection." People used to complain
that OOP made the flow of control less obvious and they were right,
but it's worth it to gain other advantages, IF they outweigh the cost
of reduced clarity! Same with this sort of aspect refactoring. The PCD
"set(String Person.name)" is more obvious and sometimes that's better.
I'm thinking of the cases when it isn't, but I hope I'm pragmatic
enough to leave well enough alone when it is better!

What I'm getting at with general annotation approaches like "@State"
or "@Identifier" is to find ways of exposing join points in a general
way that cover a lot of potential aspects without undo assumptions
about possible cross-cutting concerns. I'm certainly still learning
when to make the tradeoffs. Also, annotations are pretty limited as a
mechanism, of course. PCDs defined by components (as you mention
below) and/or "improved" kinds of interfaces will no doubt be
essential.

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.

Hmm. Not really. I'm pragmatic about what works ;)

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.

true...

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).

I never disagreed with your view on why PCDs are fragile; sorry if I
didn't make that clear. I also agree about the tradeoff's of power vs.
fragility. I'm just groping for ways to reduce the problems to the
fewest possible points of breakage and help identify better patterns
and idioms that support this.

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.

Yes.

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, ...

Right. This is essentially all I'm after.

... 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
[ Did your reply get cut off here? That's all I got.]

Yes, that's still a potential problem, but it seems to me that you've
just made the overall problem smaller and more tractable, especially
for large-scale systems where this dependency inversion was worth the
trouble. Now you've got a more conventional "namespace" problem that
can be solved using well known means. At least I hope that's true for
the general case and not just your example.

Thanks for keeping me on my toes ;)

dean
--
Dean Wampler
http://www.aspectprogramming.com
http://www.newaspects.com
http://www.contract4j.org


Back to the top