James Gosling says about the concrete application of aspects some years ago:
... And because theyre narrowly focused they actually manage to be really safe and useful. [Otherwise,] it feels like handing razor blades to children. Youve got to have razor blades to shave your beard, but oh my God, dont let five-year-olds do craft projects with them. And of course most of the people out there using AOP arent 5-year-olds doing craft projects, but theyre easily tempted. It just seems to me that that theres just a better way....
I won't do without aspects in the meantime, but my conclusion of what James Gosling said is that we should be careful by using them...
The implemetation of an interface (a set of methods) that represent a crosscutting concern is - in my opinion - one main use case for AOP (called inter-type declaration). But if you won't be lost in the jungle of somewhere hidden pointcut declarations, such an inter-type declaration should be visible at the classes where the aspect is to be apllied. Furhermore I want to let the developer decide which class should implement such an interface/aspect. This can be achieved by an annotation that may have the same name as the interface to be implemented by the aspect. Instead of adding the interface as
implements
you simply define the annotation.I will take an unique aspect as an example.
All persitent entitites of this project will be unique. Therefore I will use a String based UUID. Such an UUID has one advantage over an database generated ID (sequence): The UUID could be created and set during instance creation and could hence even be available when an (persistent) entity is still not persistent. In the GUI you have often to handle such non persistent entities. Especially when these instances are sent over the wire (by remote access) you can't rely on Java's base implementation of object identity to check wether two (non persistent) instances are equal.
The usage of an unique annotation would look like this:
... @ImplementsUnique public class UniqueEntityByAnnotation { ... }
Thats all. By using this annotation the class will implement a uniqueId String field with getter and setter and suitable equals() and hashCode()method-implementations. This functionality is provided by a IUnique interface:
... public interface IUnique { /** * Returns the unique id. */ String getUniqueId(); /** * Sets the unique id. * * @param aUniqueId * the unique id to set. */ void setUniqueId(String aUniqueId); /** * ... */ int hashCode(); /** * ... */ boolean equals(Object obj); }
The implementation of uniqueness is provided by the simple Java class Unique that may look like this:
/** * Implementation of simple uniqueness. * * @author Jörg Groß * */ public class Unique implements IUnique { /** * the uniqueId. */ protected String uniqueId; /** * {@inheritDoc} */ @Override public final String getUniqueId() { return this.uniqueId; } /** * @{inheritDoc */ public void setUniqueId(String aUniqueId) { this.uniqueId = aUniqueId; } // implements equals() and hashCode() ... }
Lets now create an aspect (using AspectJ) that will be apllied by the @ImplementsUnique annotation:
import de.jgros.eercp.core.type.IUnique; ... public privileged aspect UniqueAspect { declare parents : @ImplementsUnique * extends Unique; }
The aspect will not automatically be apllied if any class implements the IUnique interface. Instead of that all classes that define an @ImplementsUnique annotation will extend the Unique class (by adding its implementation as a mixin using AspectJ).
In my opinion the main advantage of this apporach is, that the developer could still use the IUnique-interface and the Unique class by simple implementation or extension.
Keine Kommentare:
Kommentar veröffentlichen