This page is dedicated to the new Design of the MetaClass.
It seems that we need 2 parts for MetaClass, one for the implementation and one for the user to customize the behavior. The reason for this is, that the customization is done by subclassing the implementation part. This means it is impossible to stay in the specification part (classes from groovy.*) to do a all day thing. Additionally the method signatures must be fixed to not to break code.
Currently we do something like:
This is very flexible, it allows interception and logging as well as transformation at the same time.
If we want to separate the two cases (runtime MetaClass, user defined MetaClass) in two classes, then one must call the other. As the user part should stay in the specification, it can't know the exact class of the runtime, or which method to call there. This means the user part must be called from the MetaClass in the runtime. Since we are not able to call "super" then we need an additonal Object, that we make the call on:
That's a bit like the continuation passing style.
If we want to split the behavior, then we could have an interface for each of the cases
Maybe it is a bit overkill, since a transformer can act as interceptor, but a logger is surely more lightweight than a interceptor or transformer.
The general disadvantage of the seperation is that we need to associate the custom MetaClass and the runtime MetaClass. Again I see two ways:
Where getChainedMetaClass behaves like above, meaning, it returns the next MetaClass, the last MetaClass is then the runtime implementation. This version means the usage is
instead of the old
but it means also, that we can keep the MetaClass interface/class very clean and don't need to add other methods but the methods required for chaining, which we could even make final. Any additional behavior is then controlled by the interfaces I have shown above. This way we could make an MetaClass that intercepts methods, but not properties or fields. Or a version that does logging only on Properties, but does not interfere with normal method invocation. I am aware that this means to have many interface.. 9 then (Interceptor + Transformer + Logger for each of Property, Field and Method) or 6 (if Transformer and Interceptor are unified)
Additionally to the traditional Design we could also have a new Design, where we don't take part in the CallChain, but produce a MetaMethod (or property).
which means this method is called in case of a cache miss, and not every time. Returning a MetaMethod we can control if we would like to add the method to the cache or not through the isCachable() method on MetaMethod. We could also change the interface a little and force the call to getMetaMethod even if there is no cache miss. The advantage is, we can still intercept methods, but in a more efficient way, because we don't need to throw a MissingMethodException. And we can use the cache directly if we do not force method selection through this clsss each time, which makes it very fast.
So my proposal is to chain the MetaClasses as shown above, to have a more or less empty MetaClass class which does only the chaining and to have interfaces controlling all aspects of the method invocation (or for properties/fields) like I have shown with logger and transformer or MetaClass2. Having metaClass.queryableMetaClass.getProperties() means to have less methods on MetaClass and since these MetaClasses do normally not add real methods. It would make sense to do so, but metaClass.getProperties() is not really a problem.